Почему мой простой компаратор сломан? - PullRequest
17 голосов
/ 04 марта 2009

У меня есть класс, который я упростил до этого:

final class Thing {
    private final int value;
    public Thing(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
    @Override public String toString() {
        return Integer.toString(value);
    }
}

Я хочу отсортировать массив этой вещи. Итак, я создал простой копмаратор:

private static final Comparator<Thing> reverse = new Comparator<Thing>() {
    public int compare(Thing a, Thing b) {
        return a.getValue() - b.getValue();
    }
};

Затем я использую форму с двумя аргументами Arrays.sort.

Это хорошо работает для моих тестовых случаев, но иногда все идет не так, когда массив заканчивается в странном, но повторяемом порядке. Как это может быть?

Ответы [ 6 ]

20 голосов
/ 04 марта 2009

Целочисленное переполнение & hellip; или, точнее, недолговечность.

Вместо этого сделайте явное сравнение:

private static final Comparator<Thing> reverse = new Comparator<Thing>() {
    public int compare(Thing a, Thing b) {
      int av = a.getValue(), bv = b.getValue();
      return (av == bv) ? 0 : ((av < bv) ? -1 : +1);
    }
};

Использование вычитания - это хорошо, если вы уверены, что разница не будет "округляться". Например, когда рассматриваемые значения ограничены, чтобы быть неотрицательными.

15 голосов
/ 04 марта 2009

Вы не можете использовать минус для создания сравнения. Вы переполнитесь, когда абсолютная разница превысит Integer.MAX_VALUE.

Вместо этого используйте этот алгоритм:

int compareInts( int x, int y ) {
  if ( x < y ) return -1;
  if ( x > y ) return 1;
  return 0;
}

Мне нравится иметь эту функцию в библиотеке для таких целей.

5 голосов
/ 04 марта 2009

При сравнении примитивов Java рекомендуется конвертировать их в их аналоги Object и полагаться на их compareTo() методы.

В этом случае вы можете сделать:

return Integer.valueOf(a.getValue()).compareTo(b.getValue())

Если сомневаетесь, используйте хорошо проверенную библиотеку.

5 голосов
/ 04 марта 2009

1001 * попробовать *

System.out.println(Integer.MAX_Value - Integer.MIN_VALUE);

Для этого необходимо вернуть положительное число в виде MAX_VALUE> MIN_VALUE, но вместо этого вывести -1

3 голосов
/ 04 марта 2009

Какие цифры ты там выбрасываешь? Если ваши числа достаточно велики, вы можете обернуть значения MIN / MAX для целых чисел и в конечном итоге попасть в беспорядок.

2 голосов
/ 04 марта 2009

Если значение a очень отрицательное, а значение b очень положительное, ваш ответ будет очень неправильным.

IIRC, Int переполнение молча оборачивается в JVM

- MarkusQ

...