公司来了个新同事,把代码耗时从 26856ms 优化到了 748ms,一顿操作猛如虎 ...

打印 上一主题 下一主题

主题 864|帖子 864|积分 2592

在两份表里找相同id的数据,许多同学会写两个for循环嵌套。这个写法效率比较低,今天来看一个进步速度的优化案例。
本篇分析的本领点其实是比较常见的,但是最近的几次的代码评审还是发现有不少兄弟没注意到。以是还是想拿出来说下。
是个什么场景呢?就是 for循环 里面还有 for循环, 然后做一些数据匹配、处置惩罚 这种场景。
我们结合实例代码来看看。场景示例:
比如我们现在拿到两个list 数据 ,一个是 User List 聚集 ;另一个是 UserMemo List聚集;
我们需要遍历 User List ,然后根据 userId 从 UserMemo List 里面取出 对应这个userId 的 content 值,做数据处置惩罚。
代码 User.java :
  1. @Data
  2. public class User {
  3.     private Long userId;
  4.     private String name;
  5. }
复制代码
代码 UserMemo.java :
  1. @Data
  2. public class UserMemo {
  3.     private Long userId;
  4.     private String content;
  5. }
复制代码
模拟数据聚集 :5W 条 user 数据 , 3W条 userMemo数据
  1. public static List<User> getUserTestList() {
  2.         List<User> users = new ArrayList<>();
  3.         for (int i = 1; i <= 50000; i++) {
  4.             User user = new User();
  5.             user.setName(UUID.randomUUID().toString());
  6.             user.setUserId((long) i);
  7.             users.add(user);
  8.         }
  9.         return users;
  10.     }
  11.     public static List<UserMemo> getUserMemoTestList() {
  12.         List<UserMemo> userMemos = new ArrayList<>();
  13.         for (int i = 30000; i >= 1; i--) {
  14.             UserMemo userMemo = new UserMemo();
  15.             userMemo.setContent(UUID.randomUUID().toString());
  16.             userMemo.setUserId((long) i);
  17.             userMemos.add(userMemo);
  18.         }
  19.         return userMemos;
  20.     }
复制代码
先看平时大家不注意的时候可能会这样去写代码处置惩罚 :

其实数据量小的话,其实没多大性能差异,不外我们还是需要知道一些本领点。我们来看看 这时候的一个耗时环境 。
相当于迭代了 5W * 3W 次 ,可以看到用时 是 26857毫秒 :

其实到这,插入个题外点,如果说每个userId 在 UserMemo List 里面 都是只有一条数据的场景。
  1. for (User user : userTestList) {
  2.     Long userId = user.getUserId();
  3.     for (UserMemo userMemo : userMemoTestList) {
  4.         if (userId.equals(userMemo.getUserId())) {
  5.             String content = userMemo.getContent();
  6.             System.out.println("模拟数据content 业务处理......"+content);
  7.         }
  8.     }
  9. }
复制代码
单从这段代码有没有问题 ,有没有优化点。显然是有的, 因为当我们从内循环UserMemo List里面找到匹配数据的时候, 没有做其他操作了。
这样内for循环会继续下,直到跑完再进行下一轮整体循环。以是,仅针对这种环境,1对1的或者说我们只需要找到一个匹配项,处置惩罚完后我们 应该使用 break 。
我们来看看加上 break 的一个耗时环境 :

耗时环境:可以看到 从 2W 多毫秒 变成了 1W 多毫秒, 这个break 加的很OK。

回到我们刚才, 平时需要for 循环里面再 for 循环 这种方式,可以看到耗时是 2万6千多毫秒。
那如果场景更复杂一定, 是for 循环里面 for循环 多个或者, for循环里面还有一层for 循环 ,那这样代码耗时真的非常恐怖。
那么接下来这个本领点是使用map 去优化 :

代码:
  1.     public static void main(String[] args) {
  2.         List<User> userTestList = getUserTestList();
  3.         List<UserMemo> userMemoTestList = getUserMemoTestList();
  4.         StopWatch stopWatch = new StopWatch();
  5.         stopWatch.start();
  6.         //使用stream() 记得一定要判空 这里没列出来,大家自己注意
  7.         Map<Long, String> contentMap =
  8.                 userMemoTestList.stream().collect(Collectors.toMap(UserMemo::getUserId, UserMemo::getContent));
  9.         for (User user : userTestList) {
  10.             Long userId = user.getUserId();
  11.             String content = contentMap.get(userId);
  12.             if (StringUtils.hasLength(content)) {
  13.                 System.out.println("模拟数据content 业务处理......" + content);
  14.             }
  15.         }
  16.         stopWatch.stop();
  17.         System.out.println("最终耗时" + stopWatch.getTotalTimeMillis());
  18.     }
复制代码
看看耗时:

为什么效果这么显著?
这其实就是时间复杂度,for循环嵌套for循环,就比如 循环每一个 user ,拿出 userId 需要在里面的循环从 userMemo list聚集里面 按顺序去开盲盒匹配,拿出第一个,看看userId ,拿出第二个,看看userId ,不停找匹配的。
而我们提前对 userMemo list聚集 做一次 遍历,转存储在map里面 。
map的取值效率 在多数的环境下是能维持接近 O(1) 的 , 毕竟数据结构摆着,数组加链表。
相当于拿到userId  想去开盲盒的时候, 根据userId 这个key  hash完能直接找到数组里面的索引标志位, 如果底下没链表(有的话O(logN)),直接取出来就完事了。

按照目前以JDK8 的hash算法,起hash冲突的环境是非常非常少见了。
最恶劣的环境,只有当 全部key 都冲突, 全都分配到一个桶里面去都占用一个位置 ,这时候就是O(n),这种情景不需要去考虑。
原文:https://blog.csdn.net/qq_35387940/article/details/129518893
更多文章推荐:
1.Spring Boot 3.x 教程,太全了!
2.2,000+ 道 Java面试题及答案整理(2024最新版)
3.免费获取 IDEA 激活码的 7 种方式(2024最新版)
以为不错,别忘了随手点赞+转发哦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天津储鑫盛钢材现货供应商

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表