我知道...我知道...这篇文章很老了,已经回答了很多。但是 Guavas Ordering 已经过时了,Java 8 的 Comparator 内置了解决很多自定义比较问题的功能。
我还想添加我的方法,以防有人有类似的需要通过对象中的多个字段来比较对象,这些字段可以为 null。
设置
让我们使用问题的示例数据。
我们有一个Address 列表,其中包含Coordinate 数据,在某些情况下可能为空。
自定义比较器
假设我们在类AddressSorter 中对列表进行排序,并且我们只想将具体对象的排序与那些为空的对象分开。我们可以使用自定义的Comparator 来实现这一点,该Comparator 会进行基本的空值检查。
public class AddressSorter {
private static final Comparator<Coordinate> COORDINATE_NULL_COMPARATOR = (c1, c2) -> {
if (c1 != null && c2 == null) {
return 1;
}
if (c1 == null && c2 != null) {
return -1;
}
return 0;
}
public List<Address> sortAddressList(List<Address> addresses) {
return addresses.stream()
.sorted(Comparator.compare(Address::getCoordinate, COORDINATE_NULL_COMPARATOR))
.collect(Collectors.toList());
}
}
在这个例子中,我们使用内置的Comparator.comparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator)
这将构建一个列表,其中null 为Coordinate 的Addresses 位于返回列表的开头。
这将完全跳过任何两个具体Coordinate 之间的任何比较
这可能看起来很奇怪,但在某些情况下跳过对象比较是有效的。例如,如果您需要通过可以为 null 的 LocalDateTimefield 分隔对象,则任何 LocalDateTime(或任何其他及时对象)与附加链接的比较都会导致意外行为。
比较 Coordinate 与 null 安全
因此,如果您需要比较 Coordinate 对象(包括 null 安全性),您可以使用自然顺序和 null 检查,如下所示:
public List<Address> sortAddressList(List<Address> addresses) {
return addresses.stream()
.sorted(Comparator.compare(Address::getCoordinate, Comparator.nullsFirst(Comparator.naturalOrder())
.collect(Collectors.toList());
}
编辑:如果您希望列表末尾带有Coordinate == null 的Addresses,也可以使用nullsLast。
链接
这样,我们还可以开始基于Addresses 的多个字段进行链式排序,例如:
public List<Address> sortAddressList(List<Address> addresses) {
return addresses.stream()
.sorted(Comparator.compare(Address::getCoordinate, Comparator.nullsFirst(Comparator.naturalOrder())
.thenCompare(Address::getId))
.collect(Collectors.toList());
}
所以你最终会得到一个列表,其中前导 Addresses 是一次包含 null 的 Coordinate 按 id 排序,然后所有 Addresses 和具体的 Coordinate 也排序id.
可比和 Apache Commons
如果您希望此行为作为 Address 的自然顺序,您可以使 Address 实现 Comparable,然后使用 Apache Commons CompareToBuilder 为例:
@Override
public int compareTo(Address address) {
return new CompareToBuilder()
.append(this.coordinate, address.coordinate, Comparator.nullsFirst(Comparator.naturalOrder())
.append(this.id, address.id)
.toComparison();
这使您可以在流中使用sorted(),因为它使用Address 的compareTo:
public List<Address> sortAddressList(List<Address> addresses) {
return addresses.stream()
.sorted()
.collect(Collectors.toList());
}