如何处理 Java 8 比较器中的空值和重复值?

How do I deal with null and duplicate values in a Java 8 Comparator?

我有一个 Photo 对象:

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

一组中的部分照片或所有照片的 poNumber 可以为空。我想根据poNumber对一组照片进行排序,让最低的poNumber出现在排序后的集合中。 poNumber 也可以在集合中重复。如果 poNumber 重复,则根据创建的顺序排序(最早创建的照片首先出现)。如果 poNumber 为 null 则根据创建排序。

我试过下面的代码:

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

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

但只要 poNumber 为空,它就会抛出 NullPointerException。如果 poNumber 不为空,则它工作正常。我该如何解决这个问题?

这会将空值放在开头

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

这会将空值放在末尾

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

否则,实现您自己的比较器来处理空值

我们需要一个将 Function<> 转换为比较器的解决方案,但以一种将所述 null 检查添加到链中的方式。

这就是 进入场景的地方:它创建一个 Comparator,将比较的 Photo 映射到 Integer 的比较,然后添加一个比较器,它自然地先将这些 Integernull 一起排序:

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

然后这个比较器与另一个考虑创建日期的比较器链接(对于相等的情况)。

按照你写的方式,照片键可以是 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

您可以使用两个参数的 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));