Странное поведение Java Set

У меня возникла проблема, в которой мне нужно изменить сопоставимое значение отсортированного набора на основе некоторого условия.

Делать что-то вроде этого:

SortedSet<T> groups;

for(T t: groups){
        t.setOrdinal(max);
}

Не будет автоматически переупорядочивать SortedSet.

Читая онлайн, я понял, что мне нужно удалить объект из набора, а затем добавить его снова. Очевидно, я не могу сделать это, перебирая набор. Поэтому я решил сделать Arraylist набора. Сделайте набор нулевым, а затем снова добавьте все элементы, чтобы они следовали порядку сортировки. Я сделал что-то вроде этого:

группы SortedSet;

List<T> groupList = new ArrayList<T>(groups);
groups = null;
for(T t: groupList){
        t.setOrdinal(max);
}

groups = new TreeSet<T>(groupList);

Но когда я проверяю набор групп, он не следует порядку сортировки на основе компаратора, который сравнивает порядковый номер объекта T

Но когда я попробовал что-то вроде этого:

группы SortedSet;

List<T> groupList = new ArrayList<T>(groups);
groups.clear();
for(T t: groupList){
        t.setOrdinal(max);
}

groups.addAll(groupList);

Я получил результат, как и ожидалось. Может кто-нибудь объяснить мне, что происходит?

Вот как я реализовал свой класс T

public class T implements Serializable, Comparable<T> {
//
int ordinal;
//getter
//setter

 @Override
  public int compareTo(T that) {
    if (that == null) {
      return 1;
    }

    return this.ordinal - that.ordinal;
  }

}

Для тех, кто хочет увидеть полную программу:

List<SmartUser> groupsList = new ArrayList<SmartUser>(groups);
groups = null;
for (SmartUser smartUser : groupsList) {
        if (smartUser.isExpired()) {
                smartUser.setOrdinal(Long.MAX_VALUE);
        }
        SmartUserQuery smartUserQuery = smartUser.getSmartUserQuery();
        if (smartUserQuery != null) {
                //Do parallel processing: of each samrtUser
        }
}

groups = new TreeSet<SmartUser>groupsList;

Правильный подход, дающий результат:

List<SmartUser> groupsList = new ArrayList<SmartUser>(groups);
groups.clear();
for (SmartUser smartUser : groupsList) {
        if (smartUser.isExpired()) {
                smartUser.setOrdinal(Long.MAX_VALUE);
        }
        SmartUserQuery smartUserQuery = smartUser.getSmartUserQuery();
        if (smartUserQuery != null) {
                //Do parallel processing: of each samrtUser
        }
}

groups.addAll(groupsList);

Спасибо.


person Global Warrior    schedule 09.10.2012    source источник
comment
Покажите короткую, но полную программу, демонстрирующую проблему. Например, мы не знаем, использовал ли ваш SortedSet<T> собственный компаратор или обычную реализацию Comparable<E>.   -  person Jon Skeet    schedule 09.10.2012
comment
Судя по тому, как вы это представили, оба способа должны были работать нормально.   -  person Sergey Kalinichenko    schedule 09.10.2012
comment
Помимо всего прочего, ваш метод compareTo не работает из-за переполнения... и показанный вами пример устанавливает порядковый номер каждого значения в одно и то же значение, поэтому любой порядок приемлем. Снова короткая, но полная программа, пожалуйста...   -  person Jon Skeet    schedule 09.10.2012
comment
Каков ваш ожидаемый результат?   -  person 卢声远 Shengyuan Lu    schedule 09.10.2012
comment
Не могли бы вы описать, что вы на самом деле пытаетесь сделать с этим? Попытка втиснуть это в SortedSet приведет только к хрупкому, легко ломаемому коду.   -  person Louis Wasserman    schedule 09.10.2012
comment
Я знаю, что это старое, но все еще числится без ответа. Не могли бы вы принять и проголосовать за мой ответ, если он кажется уместным? Спасибо.   -  person kriegaex    schedule 09.06.2014


Ответы (2)


Измените свой метод compareTo на ниже

@Override
public int compareTo(CustomObject o) {
    return Integer.valueOf(o.ordinal).compareTo(ordinal );
}

Также в соответствии с контрактом TreeSet вы должны предоставить метод equals, который будет синхронизирован с методом compareTo.

 @Override
public boolean equals(Object obj) {
    if (obj == null)
        return false;
    if (!(obj instanceof CustomObject))
        return false;
    CustomObject o = (CustomObject) obj;

    return this.ordinal == o.ordinal;
}

Пример реализации

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class CustomObject implements Comparable<CustomObject> {

private int ordinal = 0;

public CustomObject(int priority) {
    this.ordinal = priority;
}

/**
 * @param args
 */
public static void main(String[] args) {

    List<CustomObject> list = new ArrayList<CustomObject>();
    list.add(new CustomObject(1));
    list.add(new CustomObject(2));
    list.add(new CustomObject(3));
    list.add(new CustomObject(6));
    list.add(new CustomObject(4));
    System.out.println("Before: " + list);
    for (CustomObject object : list) {
        if (object.ordinal == 4) {
            object.ordinal = 10;
        }
    }
    Set<CustomObject> set = new TreeSet<CustomObject>();
    set.addAll(list);
    System.out.println("After: " + set);

}

@Override
public int compareTo(CustomObject o) {
    return Integer.valueOf(o.ordinal).compareTo(ordinal);
}

@Override
public boolean equals(Object obj) {
    if (obj == null)
        return false;
    if (!(obj instanceof CustomObject))
        return false;
    CustomObject o = (CustomObject) obj;

    return this.ordinal == o.ordinal;
}

@Override
public String toString() {
    return " Ordinal Value is :" + ordinal;
}

}
person Amit Deshpande    schedule 09.10.2012
comment
вам нужно переопределить метод .equals, но это также означает, что вы также должны переопределить метод .hashcode, иначе вся ваша операция будет нарушена. Хэш-наборы работают с внутренней хэш-картой, и если метод .equals и метод .hashcode не согласованы (т. е. любые поля, используемые для определения равенства, должны быть частью генерации хэш-кода), структура данных разрушается. В вашем случае вы можете делегировать метод Integer.valueOf(ordinal).hashcode() - person Matt; 10.10.2012
comment
@Matt Вопрос был о TreeSet, поэтому теперь я включил hashCode, поскольку хеширование требуется только для HashSet и HashMap. Но да, это хорошо, и это легко добавить. - person Amit Deshpande; 10.10.2012
comment
Я думаю, что мой метод compareTo в порядке, потому что я уверен, что порядковый номер всегда будет неотрицательным. - person Global Warrior; 10.10.2012

Я понял, что мне нужно удалить объект из набора, а затем добавить его снова. Очевидно, я не могу сделать это, перебирая набор. Поэтому я решил сделать Arraylist набора.

Взгляните на мой ответ на поддержание сортировки TreeSet по мере изменения значения объекта и попробуйте мою UpdateableTreeSet реализацию. Это позволяет выполнять отложенные обновления при повторении отсортированного набора.

person kriegaex    schedule 27.10.2012