Java Stream类型不匹配:无法将Map<Object,Object>转为Map<Long,Integer>
下面的命令式代码可以正常编译运行:
Map<Long, Integer> tempMap = new HashMap<>(); for (FiltersDataChain filtersDataChain : changedFiltersChainsListSet) { for (Entry<Long, Integer> entry : mariaEventService.getEventsUnderFilter(filtersDataChain).entrySet()) { tempMap.putIfAbsent(entry.getKey(), entry.getValue()); } }
但改写为以下函数式代码时编译报错:
Map<Long, Integer> tempMap2 = changedFiltersChainsListSet.stream() .map(f -> mariaEventService.getEventsUnderFilter(f).entrySet()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
报错信息:
Type mismatch: cannot convert from Map<Object,Object> to Map<Long,Integer>
补充说明:getEventsUnderFilter(f)方法返回值为Map<Long, Integer>,方法签名如下:
Map<Long, Integer> it.fox.hermes.services.MariaEventService.getEventsUnderFilter(FiltersDataChain filtersDataChain) // 返回该过滤器关联的事件列表 // 参数:filtersDataChain - 要分析的过滤器 // 返回值:事件ID与msn的映射表
问题原因
命令式代码是两层循环:外层遍历changedFiltersChainsListSet中的每个FiltersDataChain,内层遍历每个getEventsUnderFilter(f)返回的Map的entrySet,最终将单个Entry存入tempMap。
而错误的函数式代码中,map(f -> ...entrySet())会把每个FiltersDataChain转换成Set<Entry<Long, Integer>>,此时Stream的元素类型是Set<Entry<Long, Integer>>,而非单个Entry。调用Collectors.toMap(Entry::getKey, Entry::getValue)时,Java无法从Set类型推断出正确的getKey/getValue方法(Set本身没有这些方法),只能将类型推断为Object,最终返回Map<Object,Object>,与目标类型不匹配。
解决方案
使用flatMap替代map,将每个Set<Entry<Long, Integer>>转换成Stream<Entry<Long, Integer>>,把多层Stream扁平化,让整个Stream的元素是单个Entry:
Map<Long, Integer> tempMap2 = changedFiltersChainsListSet.stream() .flatMap(f -> mariaEventService.getEventsUnderFilter(f).entrySet().stream()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
注意:如果存在重复Key,默认的Collectors.toMap会抛出IllegalStateException。要和命令式代码的putIfAbsent逻辑完全一致(保留首次出现的Key值),需添加冲突处理逻辑:
Map<Long, Integer> tempMap2 = changedFiltersChainsListSet.stream() .flatMap(f -> mariaEventService.getEventsUnderFilter(f).entrySet().stream()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (oldValue, newValue) -> oldValue));
内容的提问来源于stack exchange,提问作者Stefano Bossi

