Java 8如何将Map<Object1,Object2>按a字段分组为Map<String,List<Object2>>
正确实现按Object1的a字段分组收集Object2列表的方案
你这里其实选错了Collector——Collectors.toMap()并不适合这种分组收集列表的场景,它主要用于将流元素映射为键值对,遇到重复键还会报错(除非你额外写合并逻辑)。咱们直接用专门处理分组的Collectors.groupingBy()就能完美解决问题,而且代码更简洁直观。
完整实现代码
// originalMap就是你的Map<Object1, Object2>实例 Map<String, List<Object2>> groupedMap = originalMap.entrySet() .stream() .collect(Collectors.groupingBy( // 分组的键:提取Object1的a字段值 entry -> entry.getKey().getA(), // 下游收集器:把每个entry的value(Object2)收集到List里 Collectors.mapping(Map.Entry::getValue, Collectors.toList()) ));
代码解释
- 分组函数:第一个参数
entry -> entry.getKey().getA()告诉Stream,我们要以Object1的a字段值作为分组的键。 - 下游收集器:第二个参数是核心——
Collectors.mapping()负责把每个Map.Entry转换成它的value(也就是Object2实例),再通过Collectors.toList()把这些Object2收集成列表。
为什么不能用toMap()?
如果硬要用toMap(),你需要额外处理重复键的合并逻辑,不然当多个Object1的a值相同时,会直接抛出IllegalStateException。比如你得这么写:
// 不推荐:写法繁琐,不如groupingBy直观 Map<String, List<Object2>> map1 = originalMap.entrySet() .stream() .collect(Collectors.toMap( e -> e.getKey().getA(), e -> new ArrayList<>(Collections.singletonList(e.getValue())), (list1, list2) -> { list1.addAll(list2); return list1; } ));
显然这种写法既不简洁,也不符合分组场景的语义,完全没必要。
扩展:如果需要对分组后的List做额外处理
比如你想对每个分组里的Object2按value1排序,可以在下游收集器里继续扩展:
Map<String, List<Object2>> groupedSortedMap = originalMap.entrySet() .stream() .collect(Collectors.groupingBy( entry -> entry.getKey().getA(), Collectors.mapping( Map.Entry::getValue, Collectors.collectingAndThen( Collectors.toList(), list -> list.stream().sorted(Comparator.comparing(Object2::getValue1)).collect(Collectors.toList()) ) ) ));
内容的提问来源于stack exchange,提问作者emoleumassi
相关产品推荐
相关产品推荐

