Равные элементы и набор деревьев

Создан статический вложенный класс, который реализует Comparable и переопределяет Object.equals таким образом, что e1.compareTo(e2)==0 и e1.equals(e2)==true не являются синонимами.

Затем я добавляю объекты в TreeSet и HashSet соответственно, используя его метод добавления.

Я ожидал, что вставка нескольких таких объектов либо в TreeSet, либо в HashSet будет успешной, поскольку оба утверждают, что они полагаются на равенство для определения уникальности, но я обнаружил, что вставка нескольких таких объектов в TreeSet завершится ошибкой, а вставка их в HashSet будет успешной.

public class Test {

    /*
     * This inner class deliberately has a compareTo method that is not
     * consistent with equals
     */
    static class TestObject implements Comparable<TestObject> {
        @Override
        public int compareTo(TestObject arg0) {
            // No two of these objects can be ordered
            return 0;
        }

        @Override
        public boolean equals(Object arg0) {
            // No two of these objects are ever equal to each other
            return false;
        }
    }

    public static void printSuccess(boolean success) {
        if (success)
            System.out.println(" Success");
        else
            System.out.println(" Failure");
    }

    public static void main(String[] args) {
        TreeSet<TestObject> testTreeSet = new TreeSet<TestObject>();
        HashSet<TestObject> testHashSet = new HashSet<TestObject>();

        System.out.println("Adding to the HashSet:");
        printSuccess(testHashSet.add(new TestObject()));
        printSuccess(testHashSet.add(new TestObject()));
        printSuccess(testHashSet.add(new TestObject()));

        System.out.println("Copying to the TreeSet:");
        for (TestObject to : testHashSet) {
            printSuccess(testTreeSet.add(to));
        }
    }
}

Вывод вышеуказанной программы

Adding to the HashSet:
 Success
 Success
 Success
Copying to the TreeSet:
 Success
 Failure
 Failure

Может ли кто-нибудь сказать мне, почему Tree set ведет себя так?


person Sachin Sachdeva    schedule 08.05.2017    source источник
comment
Возможно, их hashCode такой же?   -  person Shark    schedule 08.05.2017
comment
@Shark — набор хэшей добавляет все объекты   -  person Sachin Sachdeva    schedule 08.05.2017
comment
Я даже не могу компилировать ваш код в демоверсии. Он жалуется, что hashCode() никогда не переопределялся. Возможно, для всех экземпляров используется один и тот же хеш-код.   -  person Tim Biegeleisen    schedule 08.05.2017
comment
@OHGODSPIDERS --API-документация для TreeSet.add(E e) is Добавляет указанный элемент в этот набор, если он еще не присутствует. Более формально, добавляет указанный элемент e в этот набор, если набор не содержит элемента e2 такого, что (e==null ? e2==null : e.equals(e2)).   -  person Sachin Sachdeva    schedule 08.05.2017
comment
Возможно, потому что вы пытаетесь добавить неупорядоченные элементы в TreeSet, который должен упорядочивать элементы.   -  person Marius Loewe    schedule 08.05.2017
comment
@TimBiegeleisen - на моей машине компилируется нормально.   -  person Sachin Sachdeva    schedule 08.05.2017
comment
Этот вопрос устарел, так как ваш контракт hashcode/equals нарушен. любые два объекта, для которых o1.equals(o2) истинно, должны иметь один и тот же хэш-код. Это также означает, что в коллекции на основе хэша нет двух объектов, для которых o1.equals(o2) является истинным.   -  person Timothy Truckle    schedule 08.05.2017
comment
@ShowStopper выглядит так, как будто JavaDoc несовместим, и они просто использовали стандартное описание для метода добавления. Если вы посмотрите на исходный код TreeSet, вы увидите, что по умолчанию он использует TreeMap для сохранения данных, а в методе put класса TreeMap он использует только методы компаратора или compareTo объектов для проверки на равенство, но никогда метод equals.   -  person OH GOD SPIDERS    schedule 08.05.2017


Ответы (3)


«Экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или сравнения), поэтому два элемента, которые считаются равными с помощью этого метода, с точки зрения набора равны». https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html И ваш compareTo говорит, что все они равны.

person Marius Loewe    schedule 08.05.2017

Возвращаемое значение 0 для compareTo означает, что объекты равны, поэтому e1.compareTo(e2) == 0 тогда и только тогда, когда e1.equals(e2) == true.

TreeSet гарантирует упорядочение, поэтому использует метод сравнения, а HashSet — нет, поэтому использует метод equals. Вместо этого попробуйте изменить метод compareTo на положительное/отрицательное число.

Подробнее об интерфейсе Compareable можно прочитать здесь .

person afifit    schedule 08.05.2017
comment
Документация API для TreeSet.add(E e) is Добавляет указанный элемент в этот набор, если он еще не присутствует. Более формально добавляет указанный элемент e к этому набору, если набор не содержит элемента e2 такого, что (e==null ? e2==null : e.equals(e2)). - person Sachin Sachdeva; 08.05.2017
comment
Из документа TreeSet: Экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или сравнение), поэтому два элемента, которые считаются равными с помощью этого метода, являются равными с точки зрения набора. Поскольку они доверяют программисту сохранить контракт Compareable, equals — это то же самое, что и compareTo, в результате чего получается 0. - person afifit; 08.05.2017

Кроме того, это явно описано в javadoc от java.util.Comparator. case и сообщает, что Comparator для SortedSet должно совпадать с equals:

Например, предположим, что кто-то добавляет два элемента a и b, такие что (a.equals(b) && c.compare(a, b) != 0) к пустому TreeSet с компаратором c. Вторая операция добавления вернет значение true (и размер набора деревьев увеличится), поскольку a и b не эквивалентны с точки зрения набора деревьев, хотя это и противоречит спецификации метода Set.add.

person Dmitriy Popov    schedule 05.10.2017