【java使用LinkedHashMap进行list数据分组写入,顺序并没有按照原始顺序,原因分析】

问题记录:

java 复制代码
/**
 * 批量装入滚动计算结果特征值信息
 * @param rollResultEigList 滚动结果列表
 */
public void putRollEigResults(List<ResourceCalcSceneSectionResultEig> rollResultEigList) {
    if (rollResultEigList == null || rollResultEigList.isEmpty()) {
        return;
    }
    
    if (rollEigResults == null) {
        rollEigResults = new LinkedHashMap<>();
    }
    
    // 按照scprodId分组,只取第一条记录
    rollResultEigList.stream()
        // 假设ResourceCalcSceneSectionResultEig有一个getScprodId()方法
        .collect(Collectors.toMap(
            ResourceCalcSceneSectionResultEig::getScprodId,  // key: scprodId
            rollResultEig -> rollResultEig,                  // value: 对象本身
            (existing, replacement) -> existing,             // 如果key重复,保留第一个
            LinkedHashMap::new                              // 保持插入顺序的Map
        ))
        .forEach((scprodId, rollResultEig) -> {
            rollEigResults.put(scprodId, rollResultEig);
        });
}

按照以上这个代码,我在数据库里面查询后的结果rollResultEigList,是按照scprodId字段降序desc排列的,为什么经过这个putRollEigResults函数变成了scprodId升序排列呢?

原因分析:

问题出在 LinkedHashMap 的插入顺序上。当使用 Collectors.toMap(..., LinkedHashMap::new) 时,它确实会保持插入顺序,但是 rollResultEigList 在经过 toMap 处理后,顺序会发生变化,因为:

HashMap的key遍历顺序:在 toMap 过程中,流会遍历 rollResultEigList 并按 scprodId 分组

合并逻辑导致顺序变化:当遇到重复的 scprodId 时,合并函数 (existing, replacement) -> existing 会保留第一个遇到的,但 HashMap的key在插入时会重新排序(虽然LinkedHashMap保持插入顺序,但key的插入顺序可能不同于原列表顺序)

更重要的是,Collectors.toMap 在处理过程中不保证保留原列表的顺序。如果想保持原列表的降序排列,需要手动处理:

java 复制代码
/**
 * 批量装入滚动计算结果特征值信息
 * @param rollResultEigList 滚动结果列表
 */
public void putRollEigResults(List<ResourceCalcSceneSectionResultEig> rollResultEigList) {
    if (rollResultEigList == null || rollResultEigList.isEmpty()) {
        return;
    }
    
    if (rollEigResults == null) {
        rollEigResults = new LinkedHashMap<>();
    }
    
    // 使用LinkedHashMap来保持插入顺序
    // 但是我们需要按照原列表的顺序插入,并且去重
    Set<Long> processedIds = new HashSet<>();
    
    for (ResourceCalcSceneSectionResultEig rollResultEig : rollResultEigList) {
        Long scprodId = rollResultEig.getScprodId();
        
        // 只添加第一次出现的scprodId,保持原列表顺序
        if (!processedIds.contains(scprodId)) {
            processedIds.add(scprodId);
            // 如果不想覆盖已存在的记录,可以添加这个判断:
            // if (!rollEigResults.containsKey(scprodId)) {
            rollEigResults.put(scprodId, rollResultEig);
            // }
        }
    }
}

更好的解决方案 - 使用 forEachOrdered 来保持流处理的顺序:

java 复制代码
public void putRollEigResults(List<ResourceCalcSceneSectionResultEig> rollResultEigList) {
    if (rollResultEigList == null || rollResultEigList.isEmpty()) {
        return;
    }
    
    if (rollEigResults == null) {
        rollEigResults = new LinkedHashMap<>();
    }
    
    // 使用LinkedHashMap手动保持顺序
    Map<Long, ResourceCalcSceneSectionResultEig> tempMap = new LinkedHashMap<>();
    
    rollResultEigList.forEach(rollResultEig -> {
        Long scprodId = rollResultEig.getScprodId();
        // 如果还没有这个key,就放入(保持第一个出现的顺序)
        tempMap.putIfAbsent(scprodId, rollResultEig);
    });
    
    // 将处理后的结果放入最终的map
    tempMap.forEach(rollEigResults::put);
}

为什么用 putIfAbsent 而不是 merge?

putIfAbsent 会检查key是否存在,不存在才放入,这样就能保持原列表中第一个出现的顺序

这相当于手动实现了 (existing, replacement) -> existing 的逻辑,同时保持了LinkedHashMap的插入顺序

这样修改后, rollEigResults 就会按照原列表 rollResultEigList 中第一次出现的 scprodId 的顺序来排列,保持降序顺序。

相关推荐
无限的鲜花4 小时前
反射(原创推荐)
java·开发语言
IT二叔4 小时前
Java项目部署-03-teamcity-cicd-docker镜像流水线方式部署
java·ci/cd·持续部署
yongche_shi4 小时前
ragas官方文档中文版(五十)
开发语言·python·ai·ragas·如何评估和改进 rag 应用
一路向北he4 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
超级数据查看器5 小时前
超级数据查看器 v10.0 发布
java·大数据·数据库·sqlite·安卓
AI行业学习5 小时前
Notepad++ 官方下载 + 完整安装 + 全套优化配置(2026最新)
开发语言·人工智能·python·前端框架·html·notepad++
折哥的程序人生 · 物流技术专研6 小时前
《Java 100 天进阶之路》第50篇:阻塞队列与并发容器(2026版)
java·面试题·java进阶·blockingqueue·并发容器·集合源码·java100天进阶
ai_coder_ai6 小时前
编写自动化脚本,在自己后端服务中使用Open Api进行设备相关操作
java·运维·自动化
大圣编程6 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
硕风和炜6 小时前
【LeetCode: 2492. 两个城市间路径的最小分数 + DFS】
java·算法·leetcode·深度优先·dfs·bfs·并查集