Java Integer compareTo () - навіщо використовувати порівняння проти віднімання?


80

Я виявив, що java.lang.Integerреалізація compareToметоду виглядає наступним чином:

public int compareTo(Integer anotherInteger) {
    int thisVal = this.value;
    int anotherVal = anotherInteger.value;
    return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}

Питання в тому, чому використовувати порівняння замість віднімання:

return thisVal - anotherVal;

27
Коли ми так швидко турбуємось про мікрооптимізацію, ми часто отримуємо код помилки.
Kevin Bourrillion

Починаючи з JDK 7, можна використовувати Integer.compare(thisVal, anotherVal)замість того, щоб виписувати тернарний вираз.
Стюарт Маркс

Відповіді:


96

Це пов’язано з переповненням цілих чисел. Коли thisValдуже велике і anotherValвід'ємне, тоді віднімання останнього від першого дає результат, який більший, ніж thisValможе перелитися до від'ємного діапазону.


Так, те, як вони це зробили тут, можливо, ефективніше, ніж перевірка на переповнення та ін
rogerdpack

Використовуйте Guava ComparisonChain. Це дуже зручно! google.github.io/guava/releases/22.0/api/docs/com/google/common/…
Андреа Бергонцо

thisValне повинен бути великим. thisValможе бути навіть нульовим і anotherValбути, Integer.MIN_VALUEі ви вже маєте переповнення. І пам’ятайте, що, звичайно, це може бути і навпаки, thisValueдуже маленьке і anotherValдосить велике, щоб відстань перевищувала intдіапазон значень.
Holger

65

Віднімання "фокусу" для порівняння двох числових значень порушено !!!

        int a = -2000000000;
        int b =  2000000000;
        System.out.println(a - b);
        // prints "294967296"

Тут, a < bще a - bпозитивно.

НЕ використовуйте цю ідіому. Це не працює.

Більше того, навіть якщо це і спрацює , це НЕ забезпечить суттєвого покращення продуктивності, а насправді може коштувати читабельності.

Дивитися також

  • Головоломки Java 65: Дивна сага про підозрілий сорт

    Ця головоломка має кілька уроків. Найбільш конкретним є: Не використовуйте порівняльник на основі віднімання, якщо ви не впевнені, що різниця між значеннями ніколи не буде більшою за Integer.MAX_VALUE . Загальніше, остерігайтеся intпереповнення. Інший урок - слід уникати "розумного" коду. Намагайтеся писати чіткий, правильний код і не оптимізуйте його, якщо це не виявиться необхідним.


2
Це насправді зовсім не зламане. Якщо ви щось знаєте про цифри, які порівнюєте, ви, мабуть, будете знати, що їх безпечно порівнювати. Навіть не знаючи, просто ((long)a - b)повинен працювати. Хоча ти маєш рацію; це дуже рідко корисно.
амара

4
@naiad просто робити ((long)a - b)не допомагає, оскільки вам доведеться повернути результат назад int, оскільки саме це має повернути компаратор, закінчуючи знову переповненням. Вам доведеться зробити щось на зразок Long.signumрезультату, про який легко забути, як показує ваш коментар. І це може бути навіть не більш ефективним, ніж те Integer.compare, з чим JVM може впоратися по суті ...
Холгер

9

Простіше кажучи, intтип недостатньо великий, щоб зберігати різницю між двома довільними intзначеннями. Наприклад, різниця між 1,5 млрд. Та 1,5 млрд. Становить 3,0 млрд., Але intне може містити значень більше 2,1 млрд.



1

На додаток до переповнення, слід зауважити, що версія із субстракцією не дає однакових результатів .

  • Перша версія compareTo повертає одне з трьох можливих значень: -1, 0 або 1.
  • Якщо замінити останній рядок на віднімання, результатом може бути будь-яке ціле значення.

Якщо ви знаєте, що переповнення не буде, ви можете використати щось подібне:

public int compareTo(Integer anotherInteger) {
    return sign(this.value - anotherInteger.valuel);
}

12
Ви праві, що результати не однакові. Але вони не зобов’язані бути! compareToпотрібно лише повернути від’ємне значення, нуль або позитивне значення, залежно від порядку сортування thisта іншого об’єкта. Дивіться java.sun.com/j2se/1.5.0/docs/api/java/lang/…
Крістіан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.