Как мне работать с нулевыми и дублирующимися значениями в компараторе Java 8?

У меня есть объект Photo:

public class Photo {
    @Id
    private String id;
    private LocalDateTime created;
    private Integer poNumber;
}

poNumber может быть нулевым для некоторых фотографий или всех фотографий в наборе. Я хочу отсортировать набор фотографий в соответствии с poNumber, чтобы наименьшее значение poNumber появилось первым в отсортированном наборе. poNumber также может дублироваться в наборе. Если poNumber дублируется, то сортируйте в соответствии с созданным (самое раннее созданное фото появляется первым). Если poNumber - ноль, тогда сортируйте согласно созданному.

Я попробовал следующий код:

Set<Photo> orderedPhotos = new TreeSet<>(
    Comparator.nullsFirst(Comparator.comparing(Photo::getPoNumber))
              .thenComparing(Photo::getCreated));

for (Photo photo : unOrderedPhotos) {
    orderedPhotos.add(photo);
}

Но он выдает NullPointerException всякий раз, когда poNumber равен нулю. Если poNumber не равен нулю, он работает нормально. Как я могу решить эту проблему?

Ответ 1

Вы можете использовать перегрузку Comparator.comparing с двумя аргументами, например:

import static java.util.Comparator.*; // for the sake of brevity

Set<Photo> orderedPhotos = new TreeSet<>(
    Comparator.comparing(Photo::getPoNumber, nullsFirst(naturalOrder()))
              .thenComparing(Photo::getCreated));

Ответ 2

Нам нужно решение, которое превращает Function<> в Comparator, но таким способом, который добавляет указанную проверку null в цепочку.

И вот, где Александр отвечает на сцену: он создает Comparator который отображает сравниваемую Photo на сравнение Integer, а затем добавляет Comparator, который, естественно, сначала упорядочивает эти Integer с null:

Comparator.comparing(Photo::getPoNumber, Comparator.nullsFirst(Comparator.naturalOrder()))

И этот компаратор затем связывается (для случая равенства) с другим компаратором, который рассматривает дату создания.

Ответ 3

Как вы и написали, ключ Фото может быть null но не более того.

Comparator.nullsFirst( // Photo could be null.
    Comparator.comparing(Comparator.nullsFirst(Photo::getPoNumber)) // poNumber could be null.
              .thenComparing(Comparator.nullsFirst(Photo::getCreated))) // created could be null

Если любой из них не может быть null вы можете удалить Comparator.nullsFirst

Ответ 4

Это установит нулевые значения в начале

Integer getPoNumber() { return poNumber == null ? Integer.MIN_VALUE : poNumber };

Это поставит нулевые значения в конце

Integer getPoNumber() { return poNumber == null ? Integer.MAX_VALUE: poNumber };

В противном случае, реализуйте свой собственный компаратор для обработки нулевых значений