Java Streams使用groupingBy收集器时如何保留元素初始顺序
问题根源
默认调用的Collectors.groupingBy()单参数/双参数重载方法,没有指定结果Map的生成逻辑时,内部默认使用HashMap存储分组结果。HashMap基于键的哈希值散列存储,本身不记录键值对的插入顺序,因此最终提取的分组值列表,分组先后顺序和原始列表中元素的出现顺序不一致。
解决方案
以下两种实现都可以严格保留原始顺序,既保证分组的先后和该分组首个元素在原列表的出现位置一致,也保证每个分组内部的元素顺序和原列表一致。
方案1:指定有序Map容器(写法简洁,推荐)
调用三参数重载的groupingBy方法,手动传入支持插入顺序保留的LinkedHashMap作为分组结果容器,替换默认的HashMap即可:
Map<List<String>, List<QueryTreeProxy>> groupedMap = treesProxyList.stream() .collect(Collectors.groupingBy( QueryTreeProxy::getMappingKeys, // 指定用LinkedHashMap存储分组,严格保留插入顺序 LinkedHashMap::new, Collectors.toList() )); List<List<QueryTreeProxy>> groupedTreesList = new ArrayList<>(groupedMap.values());
- 如果后续不需要对分组列表做增删修改,最后一行也可以简化为
List<List<QueryTreeProxy>> groupedTreesList = groupedMap.values().stream().toList();
注意不要误用
TreeMap作为分组容器:TreeMap会按照键的排序规则重新排列分组顺序,不会保留原始插入顺序,不符合当前需求。
方案2:流内直接顺序收集(无需保留中间Map)
如果业务逻辑中不需要复用分组后的Map对象,可以直接在流收集阶段按遍历顺序生成分组列表,减少中间变量:
List<List<QueryTreeProxy>> groupedTreesList = treesProxyList.stream() .collect( LinkedHashMap<List<String>, List<QueryTreeProxy>>::new, (groupMap, item) -> groupMap.computeIfAbsent(item.getMappingKeys(), key -> new ArrayList<>()).add(item), (leftMap, rightMap) -> rightMap.forEach((key, rightList) -> leftMap.merge(key, rightList, (leftList, subList) -> { leftList.addAll(subList); return leftList; }) ) ) .values() .stream() .toList();
内容的提问来源于stack exchange,提问作者riddleme
相关产品推荐
相关产品推荐

