关于Comparator.nullsFirst()和nullsLast()报NullPointerException的坑

在使用Java的Comparator的nullsFirst()和nullsLast()方法处理排序时,可能会遇到NullPointerException。本文介绍了这两个方法的原理,分析了报错的原因,并提出了解决方案,包括使用.filter()过滤、自定义比较器以及通过自定义的mapOrDefault()方法来避免空指针异常。

引言:

本文仅为刚毕业时自己写代码遇到何发现这个问题,然后去想的解决办法的一个过程记录。也并非最优解,很高兴评论区的大佬们,能独立思考,给出更优雅的解决办法。但是本文主要是做一个记录,也不准备再修改文章内容了。借鉴着查看吧

昨天开发的时候,遇到了一个排序的问题,于是乎采用java.util包下面的Comparator.comparing来比较。测试的时候发现了空指针异常,于是乎,找到了它的nullsFirst()和nullsLast()两个方法,两个方法的意思就是,为空的时候,就给放到最前面或者最后面。但是,这两个方法并不可行,还是报错。
在这里插入图片描述
在这里插入图片描述
开启Debug。

nullsFirst()和nullsLast()方法介绍及出错原因

nullsFirst():

此方法返回比较器,其是空型比较,并认为空值小于非空。null首先通过以下逻辑进行操作:
1.null元素被认为小于non-null(即值是null的小于非空的)。
2.当两个元素都为空时,则认为它们相等。
3.当两个元素都不为空时,指定的Comparator确定顺序。
4.如果指定的比较器为null,则返回的比较器将所有非null元素视为相等。
5.如果指定的比较器可序列化,则返回的比较器可序列化。

nullsLast():

方法返回比较器,其是空型比较,并认为比非空更大空值。null首先通过以下逻辑进行操作:
1.null元素被认为大于非null。
2.当两个元素都为空时,则认为它们相等。
3.当两个元素都不为空时,指定的Comparator确定顺序。
4.如果指定的比较器为null,则返回的比较器将所有非null元素视为相等。
5.如果指定的比较器可序列化,则返回的比较器可序列化。

网上说可以 用这两个东西来避免空指针异常。但是事实并不是,在这里点名感谢w哥,大半夜不赔老婆来帮我分析问题。

看源码我们可以发现,compare是有判空两个比较对象都为空的情况,直接返回0,理论上是没有问题的,但是而debug的时候,发现这两个对象,传过来的就是对象本身,并不是我想比较的两个对象里面的某个字段。
在这里插入图片描述
所以这里判空,肯定不是null,从而继续比较,
继续往下我们可以发现,它进入了String 的compareTo()方法,但是可以发现这个方法,本身参数就有一个@NotNull注解,于是乎,当比较的字段为null的时候,就有了空指针异常。
在这里插入图片描述

解决办法

知道了原因,再来找解决办法,就有思路了

.filter()过滤

刚开始的时候,我们尝试了在流式操作的时候,用.filter()来过滤掉,字段为空的数据,实际效果也可行,确实解决了空指针异常的问题。
但是运行出来的数据,并不是我们想要的效果

自定义比较器

这个方法也是w哥提供给我的一个思路,但是,写到一半,感觉不太可行,毕竟String 的compareTo()还是有@NotNull标签,这也就意味着要写一大堆if else 自己判空,写出来的代码,很不优雅,over

最终解决方案–自己写个Function

既然它本身是因为为空的时候,有空指针异常,那么我们自己写一个方法不就好了?太伟了

 //定义排序规则
Comparator<DeviationByTypeVO> byOne = Comparator
	.comparing(mapOrDefault(DeviationByTypeVO::getOneClass));
Comparator<DeviationByTypeVO> byTwo = Comparator
	.comparing(mapOrDefault(DeviationByTypeVO::getTwoClass));
Comparator<DeviationByTypeVO> byThree = Comparator
	.comparing(mapOrDefault(DeviationByTypeVO::getThreeClass));
Comparator<DeviationByTypeVO> byFour = Comparator
	.comparing(mapOrDefault(DeviationByTypeVO::getFourClass));

//联合排序
Comparator<DeviationByTypeVO> finalComparator = 
	byOne.thenComparing(byTwo).thenComparing(byThree).thenComparing(byFour);

return result.stream().sorted(finalComparator).collect(Collectors.toList());

自定义的mapOrDefault()方法

private Function<DeviationByTypeVO,String> mapOrDefault(Function<DeviationByTypeVO, String> func){
	//用Optional来设置默认值,如果为空就设置为你想要的规则,比如我这里,想放最后
	//这里比较的因为都是英语单词,Z就是最大的,ASCII码是90,比Z大1的就是91
	return vo -> Optional.ofNullable(func.apply(vo)).orElse("91");
}

经实践证明,此方法科学可行,靠谱。

评论 13
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值