lambda的toMap是不是要注意点,线上事故

异常回顾

先看代码:

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,要么控制不要重复,否则就半夜应急吧。

经验分享 程序员 微信小程序 职场和发展