异常回顾
先看代码:
dbTaxiDrivers.ifPresent((drivers) -> {
map.putAll(drivers.stream()
.collect(Collectors.toMap(TaxiDriverInfo::getOperationId, item -> item)));
});
相信很多为了减少2层for循环,for循环查sql,都会定义Map<String, Object>的类型,去减少嵌套
而java8提供了toMap,简直就是方便。
相信很多人都用过这个方法,那这段代码到底有没有问题?我最开始以为没有,不就是转换成map吗,能有什么问题,至到吃过亏了,才知道其它有个坑在这,稍不注意就掉坑了。
这不,前阵子有个同事又踩坑了,还是大坑。
上线后,第一天,相安无事
第二天,线上狂报警: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会报主键重复的错误
啥,我怎么知道key重复
源码追踪
源码中看下报错的异常。
根据堆栈,得到报错的地方,HashMap里面的mergeFunction
从上面的代码里面可以看出,如果有值重复,则会执行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要小心,主键重复处理的情况,如果有重复,要么指定mergeFunction,要么控制不要重复,否则就半夜应急吧。