Java 11 Stream不使用groupBy实现嵌套Map收集方案咨询
解答
完全可以仅通过Collectors.toMap()实现需求,无需编写冗余遍历逻辑。你之前的写法存在三处错误:
- 在外层toMap的value映射位置直接传入
Collectors.toMap()收集器实例,而非实际的Map结构值,属于API误用 - 未指定键重复时的合并规则,
toMap默认遇到重复键会直接抛出IllegalStateException - 未实现同组元素向List追加的逻辑,无法完成元素归集
纯Stream实现(Java 11兼容,无groupingBy依赖)
该实现使用HashMap作为存储容器,天然支持personalId、rowNumber为null的场景,规避Collectors.groupingBy()的null值兼容问题:
Map<String, Map<Integer, List<MyObject>>> result = myList.stream() .collect(Collectors.toMap( // 外层Map键取personalId MyObject::getPersonalId, // 首次匹配到某个personalId时,初始化内层Map和对应元素列表 currentObj -> { Map<Integer, List<MyObject>> innerMap = new HashMap<>(); List<MyObject> initList = new ArrayList<>(); initList.add(currentObj); innerMap.put(currentObj.getRowNumber(), initList); return innerMap; }, // 遇到重复personalId时,合并两个内层Map的内容 (existingInner, newInner) -> { newInner.forEach((rowNum, newList) -> { existingInner.merge(rowNum, newList, (existList, addList) -> { existList.addAll(addList); return existList; }); }); return existingInner; }, // 指定外层Map使用HashMap实现,保证null键兼容 HashMap::new ));
更简洁的遍历实现(同样无groupingBy依赖)
如果追求代码可读性,直接使用Map的computeIfAbsent方法遍历实现逻辑更直观,性能和Stream实现无本质差异,同样支持null键:
Map<String, Map<Integer, List<MyObject>>> result = new HashMap<>(); for (MyObject obj : myList) { result.computeIfAbsent(obj.getPersonalId(), k -> new HashMap<>()) .computeIfAbsent(obj.getRowNumber(), k -> new ArrayList<>()) .add(obj); }
两种实现最终生成的数据结构完全符合需求:外层键为personalId,内层键为rowNumber,对应值为匹配的MyObject列表,全程未使用
Collectors.groupingBy(),不存在该收集器的null值兼容问题。
内容的提问来源于stack exchange,提问作者treo
相关产品推荐
相关产品推荐

