Java优化:双重for循环

在工作中,经常性的会出现在两张表中查找相同ID的数据,许多开发者会使用两层for循环嵌套,虽然实现功能没有问题,但是效率极低,一下是一个简单的优化过程,代码耗时凑从26856ms优化到了748ms。

功能场景


有两份List类型的数据,分别是UestList(用户表)和AccountList(账户表),要根据用户的id从AccountList表中查找对应的账户信息,并进行后续的处理,示例如下:

存数据(伪代码):5w条user数据,3w条Account数据

java 复制代码
 @Data
    class User{
        private Long userId;
        private String name;
    }

    @Data
    class Account{
        private Long userId;
        private String content;
    }

    public class NestedLoopOptimization{

        public static List<User> getUserList(){
            List<User> users =new ArrayList<>();
            for(inti =1; i <=50000; i++) {
                User user =newUser();
                user.setName(UUID.randomUUID().toString());
                user.setUserId((long) i);
                users.add(user);
            }
            return users;
        }

        public static List<UserMemo> getAccountTestList(){
            List<Account> accountList =newArrayList<>();
            for(inti =30000; i >=1; i--) {
                Account account =new Account();
                account.setContent(UUID.randomUUID().toString());
                account.setUserId((long) i);
                accountList.add(account);
            }
            return accountList;
        }

        // ... 后续代码
最直接的实现方式(未优化的代码):
java 复制代码
public static void nestedLoop(List<User> usersList, List<UserMemo> accountList) {
    for (User user : usersList) {
        Long userId = user.getUserId();
        for (Account account : accountList) {
            if (userId.equals(account.getUserId())) {
                String content = account.getContent();
                // System.out.println("模拟数据content 业务处理......" + content); // 避免打印影响测试结果
            }
        }
    }
}

耗时:约数万毫秒,效率很低,数据量小的话无关紧要,如果随着系统的迭代数据量骤增的时候,就会极其耗时。

第一步优化:添加break

每个userId在AccountList中只有一条对应的数据,所以找到匹配项之后就可以跳出内循环:

java 复制代码
public static void nestedLoop(List<User> usersList, List<UserMemo> accountList) {
    for (User user : usersList) {
        Long userId = user.getUserId();
        for (Account account : accountList) {
            if (userId.equals(account.getUserId())) {
                String content = account.getContent();
                // System.out.println("模拟数据content 业务处理......" + content); // 避免打印影响测试结果
                break;
            }
        }
    }
}

第一步优化结束之后任需要很多耗时,但是比起嵌套循环好很多。

第二步优化:使用Map优化

java 复制代码
public static void mapOptimizedLoop(List<User> userTestList, List<UserMemo> accountList) {
        Map<Long, String> contentMap = accountList.stream().collect(Collectors.toMap(UserMemo::getUserId, UserMemo::getContent));

        for (User user : userTestList) {
            Long userId = user.getUserId();
            String content = contentMap.get(userId);

            if (StringUtils.hasLength(content)) {
               // System.out.println("模拟数据content 业务处理......" + content); // 避免打印影响测试结果
            }
        }
    }

做以上优化之后,耗时显著减少,通常在数百毫秒级别。

原理:

两层 for 循环嵌套的时间复杂度为 O(n*m),其中 n 和 m 分别为两个列表的长度。使用 Map 后,get 操作的时间复杂度接近 O(1),整体时间复杂度降为 O(n+m),避免了内循环的重复遍历。HashMap 的 get 方法内部使用了 getNode 方法来查找键值对。getNode 方法利用哈希表结构,快速定位到目标键值对。虽然在极端情况下(所有键的哈希值都相同),getNode 的时间复杂度会退化为 O(n),但在实际应用中,哈希冲突的概率很低,HashMap 的 get 操作效率通常很高。因此无需过于担心 O(n) 的最坏情况。

通过以上优化之后,可以显著的提高代码的执行效率,已经其健壮性,尤其是在处理大数据量的时候,使用Map优化,可以带来巨大的性能提升,避免了不必要的计算,从而实现了代码的性能。

相关推荐
马优晨4 小时前
JAVA 中 VO 实体类是什么意思,为什么要做这个
java·开发语言·状态模式·java vo·java vo是什么
Aurorar0rua5 小时前
CS50 x 2024 Notes Python - 01
开发语言·python·学习方法
Baron X5 小时前
Eclipse Temurin 21 (JRE) Alpine 镜像获取、离线导入与推送操作
java·docker·eclipse·temurin
孙浩君5 小时前
我用 Spring Boot + xterm.js 写了个终端风格互动叙事游戏引擎:游戏即文件,上传一个 JSON 就能玩
java
jose5 小时前
URL 里多写了一个端口,为什么请求还能调通?
java·后端
孙浩君5 小时前
我用 Spring Boot 写了个「终端风格模拟经营游戏引擎」:引擎 + JSON 游戏文件,开箱即玩
java
茉莉玫瑰花茶5 小时前
GO [ 标准库 ]
开发语言·后端·golang
y1su6 小时前
Leetcode 动态规划 模板
java·数据结构·算法·leetcode·动态规划
半杯咖啡半行码6 小时前
C++11核心特性详解:从C++11到C++20 的现代C++指南
开发语言·c++
Wang's Blog6 小时前
Java 项目实战: 交友项目 - JWT 概述:为什么要用 Token 换掉 Session
java·ui·交友