Интерфейс компаратора Java

У меня неправильное представление об интерфейсе Comparator и его методе сравнения вот следующий код, и мне интересно, почему метод сравнения возвращает -33, я считаю, что он должен возвращать 33

import java.util.*;
public class Sorted implements Comparable<Sorted>, Comparator<Sorted> {
private int num;
private String text;

Sorted(int n, String t) {
    this.num = n;
    this.text = t;
}

public String toString() {
    return "" + num;
}

public int compareTo(Sorted s) {
    return text.compareTo(s.text);
}

public int compare(Sorted s1, Sorted s2) {
    System.out.println(s1.num-s2.num); // return -33
    return s1.num - s2.num;
}
public static void main(String[] args) {
    Sorted s1 = new Sorted(88, "a");
    Sorted s2 = new Sorted(55, "b");
    TreeSet<Sorted> t1 = new TreeSet<>();
    t1.add(s1); t1.add(s2);
    TreeSet<Sorted> t2 = new TreeSet<>(s1);
    t2.add(s1); t2.add(s2);
    System.out.println(t1 + " " + t2);
    System.out.println(s1.num-s2.num); // prints 33
} }

s1 и s2 в compare(Sorted s1, Sorted s2) являются определениями локальных переменных, вы не должны путать их с определениями в main(). Не определено (алгоритмически, только реализацией), как TreeSet сравнивает два элемента.

leonardkraemer 16.02.2019 11:38

конечно, я согласен с вами. но это то, что я пытаюсь выяснить (как TreeSet сравнивает элемент буксировки)

user2780962 16.02.2019 11:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
119
2

Ответы 2

Вы наверняка знаете, что если a-b=c, то b-a=-c.

То, что здесь происходит, очень похоже. Вы, кажется, предположили, что TreeSet вызывает метод compare следующим образом:

comparator.compare(s1, s2)

(Обратите внимание, что я использовал s1 и s2 в демонстрационных целях. Очевидно, что они не входят в область применения TreeSet. s1 — это тот же экземпляр, что и ваш s1, а s2 — тот же экземпляр, что и ваш s2`.)

Но он может вызывать compare и так:

comparator.compare(s2, s1)

не так ли?

Если он назвал это вторым способом, то следует ожидать результата -33.

Обновлено:

Я просмотрел исходный код TreeSet.add и обнаружил, что он вызывает TreeMap.put с элементом, который вы добавляете в качестве ключа. Если вы посмотрите дальше в TreeMap.put, вы найдете:

Comparator<? super K> cpr = comparator;
if (cpr != null) {
    do {
        parent = t;
        cmp = cpr.compare(key, t.key); // <--- "key" is the key passed into this method
                                       // "t" is an element that is already in the map
        if (cmp < 0)
            t = t.left;
        else if (cmp > 0)
            t = t.right;
        else
            return t.setValue(value);
    } while (t != null);
}

Это показывает, что TreeSet действительно вызывает compare так, как я описал.

Обновлено:

Как сказал Хольгер в комментариях, вы не должны реализовывать Comparator, вычитая два целых числа. Вместо этого вы должны использовать Integer.compare:

return Integer.compare(s1.num, s2.num);

На самом деле нет необходимости реализовывать Comparator вообще, вы можете передать Comparator.comparingInt(s -> s.num) при создании TreeMap:

TreeSet<Sorted> t1 = new TreeSet<>(Comparator.comparingInt(s -> s.num));

я согласен с вами, но в некоторых других программах это называется обычным способом, а не наоборот, откуда я знаю, как Treeset вызывает метод сравнения

user2780962 16.02.2019 11:40

@user2780962 user2780962 Вам не нужно это знать, если только вы сами не пытаетесь написать TreeSet. В методе compare ваша единственная задача — сравнить два объекта. Вы не должны зависеть от порядка аргументов. Если вам просто интересно, я добавил часть исходного кода, который показывает, как вызывается compare.

Sweeper 16.02.2019 11:45

@user2780962 user2780962 Что вам нужно понять, так это то, что вы не могу полагаетесь на любой заказ. Вы должны предоставить хороший компаратор, вот и все.

Jean-Baptiste Yunès 16.02.2019 11:46

@ user2780962 Мой ответ ответил на ваш вопрос? Если это так, пожалуйста, примите его, нажав на эту галочку!

Sweeper 16.02.2019 12:13

Кроме того, вы все равно не должны использовать минус для компаратора. Разница между двумя значениями int может превышать диапазон значений int, следовательно, происходит переполнение. В качестве дополнительного углового случая, когда разница составляет ровно Integer.MIN_VALUE, она будет асимметричной. Правильным решением будет вызов Integer.compare(s1.num, s2.num), однако сомнительно, чтобы этот класс все равно реализовывал Comparator. Используйте new TreeSet<>(Comparator.comparingInt(s -> s.num)), чтобы создать набор, отсортированный по num.

Holger 18.02.2019 09:29

@Holger Вы правы, но это не вопрос ОП, не так ли? Вот почему я не включил его в ответ.

Sweeper 18.02.2019 09:32

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

Holger 18.02.2019 09:34

@Холгер Хорошо. Отредактировано.

Sweeper 18.02.2019 09:38

s1 и s2 в compare(Sorted s1, Sorted s2) являются определениями локальных переменных, вы не должны путать их с определениями в main(). Не определено (алгоритмически, только реализацией), как TreeSet сравнивает два элемента.

compare(s1, s2) //yields 33
compare(s2, s1) //yields -33

TreeSet внутренне использует TreeMap. put вызовы сравниваются в нескольких местах, обычно с элементом, который вы помещаете в TreeSet в качестве первого элемента. Поэтому put(s2)позвонит compare(s2, s1). См. фрагмент кода ниже:

public V put(K key, V value) {    
        Entry<K,V> t = root;    
        if (t == null) {
            compare(key, key); // type (and possibly null) check       
            root = new Entry<>(key, value, null);  
            size = 1;
            modCount++;    
            return null;    
        }    
        int cmp;    
        Entry<K,V> parent;    
        // split comparator and comparable paths    
        Comparator<? super K> cpr = comparator;    
        if (cpr != null) {    
            do {
                parent = t;    
                cmp = cpr.compare(key, t.key);    
                if (cmp < 0)   
                    t = t.left;    
                else if (cmp > 0)    
                    t = t.right;    
                else    
                    return t.setValue(value);    
            } while (t != null);    
        }    
        else {   
            if (key == null)    
                throw new NullPointerException();    
            @SuppressWarnings("unchecked")    
                Comparable<? super K> k = (Comparable<? super K>) key;   
            do {    
                parent = t;    
                cmp = k.compareTo(t.key);    
                if (cmp < 0)
                     t = t.left; 
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);    
            } while (t != null);    
        }    
        Entry<K,V> e = new Entry<>(key, value, parent);    
        if (cmp < 0)
                parent.left = e;    
        else    
            parent.right = e;    
        fixAfterInsertion(e);    
        size++;    
        modCount++;

        return null;
    }

Другие реализации или методы могут иметь другое поведение.

Другие вопросы по теме