目录
先看一串代码
dbTaxiDrivers.ifPresent((drivers) -> {
map.putAll(drivers.stream()
.collect(Collectors.toMap(TaxiDriverInfo::getOperationId, item -> item)));
});
里面用到了java8的高级收集器:toMap
相信很多人都用过这个方法,那这段代码到底有没有问题?我最开始以为没有,不就是转换成map吗,能有什么问题,至到吃过亏了,才知道其它有个坑在这,稍不注意就掉坑了。
这不,前阵子有个同事又踩坑了,还是大坑
不平凡的2020
今年的春节来的格外早。
然后又是百年不遇的新冠疫情,但是(注意这个但是!一般这个词出现的时候,就是要搞事情了),班还是要上的。
为了抗击疫情,我司也开发了一个疫情模块,在APP上为海外同胞提供二手数据(估计也是爬来的)。
所以就有了类似上面的一串代码,其中toMap出场了:Collectors.toMap
上线后,第一天,相安无事,皆大欢喜,都准备写PPT吹水了。。。
第二天,线上狂报警:XX模块迭0,一看日志,出现了类似如下错误:
java.lang.IllegalStateException: Duplicate key a
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
主键冲突!!!
如果Map的key里面有重复值,Collectors.toMap会报主键重复的错误
mergeFunction
源码中看下报错的异常
根据堆栈,得到报错的地方,HashMap里面的mergeFunction
从上面的代码里面可以看出,如果有值重复,则会执行mergeFunction,默认为抛出异常
那么如果出现主键重复的情况,该如何处理呢?
指定冲突处理函数
默认的处理函数是抛出函数
再来看看另外一个函数toMap,就是可以手动传入mergeFunction
重写一下上面那个例子
List<String> list = Lists.newArrayList();
list.add("a");
list.add("a");
Map<String, String> map = list.stream().collect(Collectors.toMap(a -> a, Function.identity(), (oldValue, newValue) -> newValue));
运行上面的例子,将会正常运行,在上面指定了mergeFunction:(oldValue, newValue) -> newValue,表示以新值覆盖旧值!
这样,开发者又可以主动处理这种情况了
小心toMap有坑
总之一句话,用toMap要小心,主键重复处理的情况,如果有重复,需要指定mergeFunction,否则就半夜应急吧