环境
Java8
数据准备
非完整代码:
List<Map<String, Object>> result = new ArrayList<>();
map1.put("created", null);
map2.put("created", 1578394193000);
map3.put("created", 1578394183000);
map4.put("created", 1578394173000);
map5.put("created", null);
上面每个map
还有name
、skuId
等字段。
null排在前面
这里,我曾经犯了个错,记录下:
public static void main(String[] args) {
List<Map<String, Object>> result = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("created", null);
Map<String, Object> map2 = new HashMap<>();
map2.put("created", Instant.ofEpochMilli(1578394193000L).atOffset(ZoneOffset.of("+8")));
Map<String, Object> map3 = new HashMap<>();
map3.put("created", Instant.ofEpochMilli(1578394183000L).atOffset(ZoneOffset.of("+8")));
Map<String, Object> map4 = new HashMap<>();
map4.put("created", Instant.ofEpochMilli(1578394173000L).atOffset(ZoneOffset.of("+8")));
Map<String, Object> map5 = new HashMap<>();
map5.put("created", null);
result.add(map1);
result.add(map2);
result.add(map3);
result.add(map4);
result.add(map5);
// 错误
List<Map<String, Object>> errorList = result.stream()
.sorted(nullsFirst(comparing(map -> (OffsetDateTime) map.get("created"))))
.collect(Collectors.toList());
for (Map<String, Object> map : mapList) {
System.out.println(map);
}
}
上面代码执行后,会报NPE
异常。
分析下原因:
看下comparing()
方法的源码:
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
当遇到null
元素时,keyExtractor.apply(c1)
就会返回null
,接着又去调compareTo()
方法时,就报NPE
异常了。
正确的代码:
// 正确
List<Map<String, Object>> mapList = result.stream()
.sorted(comparing(map -> (OffsetDateTime)map.get("created"), nullsFirst(OffsetDateTime::compareTo)))
.collect(Collectors.toList());
而这段代码不报异常的原因:
先看下comparing()
方法的源码:
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
当代码执行到:
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2))
会执行Comparators
类中的compare()方法:
@Override
public int compare(T a, T b) {
if (a == null) {
return (b == null) ? 0 : (nullFirst ? -1 : 1);
} else if (b == null) {
return nullFirst ? 1: -1;
} else {
return (real == null) ? 0 : real.compare(a, b);
}
}
可以看到,这个方法加了空判断。
当执行到real.compare(a, b)
这个方法的时候,就会去执行我们传入的方法引用OffsetDateTime::compareTo
。
降序并且null排在前面
刚开始我是这么写的:
List<Map<String, Object>> mapList = result.stream()
.sorted(comparing(map -> (OffsetDateTime)map.get("created"),
nullsFirst(OffsetDateTime::compareTo).reversed()))
.collect(Collectors.toList());
但是这样的会,结果就是:
{created=2020-01-07T18:49:53+08:00}
{created=2020-01-07T18:49:43+08:00}
{created=2020-01-07T18:49:33+08:00}
{created=null}
{created=null}
降是降序了,但是null缺跑最后去了。
看下源码
nullsFirst(OffsetDateTime::compareTo)
上面那段代码点进去后:
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}
我们再看下Comparators
类中reversed()
方法:
@Override
public Comparator<T> reversed() {
return new NullComparator<>(!nullFirst, real == null ? null : real.reversed());
}
发现reversed()
方法第一个参数使用了!nullFirst
;
所以正确的写法应该是:
// 正确
List<Map<String, Object>> mapList = result.stream()
.sorted(comparing(map -> (OffsetDateTime)map.get("created"),
nullsLast(OffsetDateTime::compareTo).reversed()))
.collect(Collectors.toList());
结果:
{created=null}
{created=null}
{created=2020-01-07T18:49:53+08:00}
{created=2020-01-07T18:49:43+08:00}
{created=2020-01-07T18:49:33+08:00}
符合要求。