Почему Comparator через вычитание дат роняет сортировку

Классическая задача на ревью: отсортировать список событий по timestamp. Кандидат пишет короткий компаратор через вычитание — выглядит элегантно, компилируется, тесты на малых данных зелёные.

Проблема всплывает только на реальных данных, когда разница между двумя long выходит за границы int. compare() обязан вернуть int, а вычитание двух far apart long просто переполняется и даёт противоречивый знак. Сортировка перестаёт быть транзитивной — на больших коллекциях Java бросит IllegalArgumentException: Comparison method violates its general contract.

// не делайте так с long/double полями Comparator byTime = (a, b) -> (int) (a.getTimestamp() - b.getTimestamp());

// безопасный вариант Comparator byTimeSafe = Comparator.comparingLong(Event::getTimestamp);

На собеседовании это часто всплывает не как прямой вопрос "найдите баг", а как продолжение задачи на сортировку: "а что если timestamp в миллисекундах с 2020 года, а не секунды с 1970" — то есть разница точно вылезет за int. Если кандидат сходу пишет compare через вычитание даже для Integer, спросят: а если оба значения — Integer.MIN_VALUE и Integer.MAX_VALUE? Там то же самое переполнение, только на int.

Comparator.comparingLong и comparingDouble существуют именно для того, чтобы не писать вычитание руками — это не стилистическое предпочтение, а единственный безопасный способ сравнить числовые поля без границы диапазона.

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки