前情提要

我们在开发中经常碰到这样的场景,查出两个 list 集合数据,需要根据他们相同的某个属性为连接点,进行聚合。但是平时我们使用的时候关注过性能吗?下面让我们一起来看看它的表现如何。

来个例子

我们现在有两个 List集合,需要根据他们相同的 personId 进行聚合处理,我们很容易想到的写法是这样的:

private static void test1(List<Person> list1, List<Person> list2) {
for (Person before:list1){
for (Person after:list2){
if(before.getPersonId().equals(after.getPersonId())){
//TODO 业务逻辑
break;
}
}
}
}

这样的代码是我们开发中最常用的一种方式,数据少的话没问题。如果数据量大的会很慢,接下来我做一个实验。看看在 1w 和 10w 的数据量下他的性能如何?

测试代码如下:

   public static void main(String[] args) {
List<Person> list1= new ArrayList<>();
List<Person> list2= new ArrayList<>();
for (int i = 0; i < 10_0000; i++) {
list1.add(Person.builder().personId(Long.valueOf(i+"")).build());
list2.add(Person.builder().personId(Long.valueOf(i+"")).build());
}
long start = System.currentTimeMillis();
test1(list1, list2);
System.out.println("for循环耗时:"+(System.currentTimeMillis()-start));

1w 耗时:343

10w 耗时:64285

仅仅 10w 的数据竟然达到了 64 秒多,可以看出它的性能是多么差了吧。

那怎么优化呢?我们可以把第二个 list 转为 map 的方式来做,示例如下:

代码如下:

private static void test2(List<Person> list1, List<Person> list2) {
Map<Long, Person> baseMap =
list2.stream().collect(Collectors.toMap(Person::getPersonId, Function.identity()));
for (Person before:list1){
Person after = baseMap.get(before.getPersonId()); }
}

接下来我们再进行下性能测试。

1w 耗时:88

10w 耗时:95

可以看出速度快了上百倍不止,如果还有小伙伴用第一种方式的话就赶紧优化了吧。

思考

我们想想第一种为什么会慢呢?

在第二个循环里他需要从 0 开始遍历所有的元素来进行比对,数据量越大,它需要遍历的数就越多,所以很慢。

所以如果我们业务上两个集合的大小和顺序一致(即能知道应该第二个循环能匹配上的元素在第几个),那么就能避免掉大量的循环。

示例如下:

我们直接在第二层循环的时候,将下标先指定为和第一层循环的一致,如果他们俩属性相同,立马跳出;进行第二次循环。

private static void test3(List<Person> list1, List<Person> list2) {
for (int i=0;i<list1.size();i++){
int jj = 0;
for (int j = i; j < list2.size(); j++) {
if (jj == list2.size()) {
break;
} if(list1.get(i).getPersonId().equals(list2.get(j).getPersonId())){
// 编写具体的逻辑
break;
}
if (j == list2.size() - 1) j = -1;
jj += 1;
}
}
}

性能测试如下:

1w 耗时:2

10w 耗时:13

我们发现又更加快了。

下面是总体的测试数据:

数据量 双层 for 循环 循环+map 改良版 for 循环
100 条数据 1 毫秒 70 毫秒 <1 毫秒
1000 条数据 16 毫秒 91 毫秒 1 毫秒
5000 条数据 66 毫秒 66 毫秒 3 毫秒
1w 条数据 208 毫秒 64 毫秒 4 毫秒
10w 条数据 62887 毫秒 84 毫秒 17 毫秒
100w 条数据 很久 155 毫秒 24毫秒

总结:如果数据量小于 5000,推荐就用双层 for 循环,如果大于 5000,则使用循环+map 的方式。

如果两个集合顺序一致,则可以用改良版的 for 循环

还在用双层for循环吗?太慢了的更多相关文章

  1. 论使用HashMap优化双层For循环的实际性能

    当需要对两个集合进行相互操作的时候,一般需要进行双层For循环,但我们知道双层For在数量越大的时候性能影响越大 这时候我们会想到的其中一种解决方法就是利用Hashmap在查找数据的高效上来优化双层F ...

  2. 高效遍历匹配Json数据与双层for循环遍历Json数据

    工作中往往遇到这种情况,保留用户操作痕迹,比如用户选择过得东西,用户进入其它页面再返回来用户选择的的数据还在. 比如:1.购物车列表中勾选某些,点击任意一项,前往详情页,再返回购物车依旧需要呈现勾选状 ...

  3. C++ 退出双层for循环,解决 break、return、continue无法实现问题

    遇到一个情景,采用双层for循环 遍历图像的像素,当找到某一个像素点满足条件时,退出双层for 循环 . 首先了解一下 continue.break.return 各自功能用法: 1.continue ...

  4. js实现99乘法表的编写(双层for循环与递归方法)

    双层for循环实现方法: function nine (num) { ; i <= num; i++){ var str = ''; ; k <= num; k++){ if(i > ...

  5. CMD批处理循环,太强大了(转)

    终极dos批处理循环命令详解格式:FOR [参数] %%变量名 IN (相关文件或命令)   DO 执行的命令 作用:对一个或一组文件,字符串或命令结果中的每一个对象执行特定命令,达到我们想要的结果. ...

  6. 还在为百度网盘下载速度太慢烦恼?chrome浏览器插件帮你解决!

    百度网盘已然成为分享型网盘中一家独大的“大佬”了.时代就是这样不管你喜不喜欢,上网总会遇到些百度网盘共享的文件需要下载.然而,百度网盘对免费用户的限速已经到了“感人”的地步了,常常十多KB/秒的速度真 ...

  7. 你只用do-while来实现循环?太浪费了!

    这是道哥的第010篇原创 目录 前言 在宏定义中的妙用 错误的宏定义 比较好的宏定义 另一个也不错的宏定义 在函数体中的妙用 函数功能:返回错误代码对应的错误字符串 函数功能:通过TCP Socket ...

  8. 反射在ADO.NET中的运用(你还在每个项目中循环遍历DataTable吗)

    图片有点大哈,但大更能说明问题.您是不是每个项目都在重复的做图片中的事情-----循环把数据库中返回的表转化为实体对象.是不是每次都在抱怨这样的重复工作.字段越多抱怨越多!不用抱怨了.当你看到这篇文章 ...

  9. python 双层for循环,在第二层的for循环中的else中的continue,会退出到第一层for循环继续执行

    for a in [1,2,3,4,5]: for b in [1,2,3]: if a == b: print("a = b = %s" % a) break # 退出本次for ...

随机推荐

  1. Linux 安装 Tomcat 详细教程

    Linux 安装Tomcat详细步骤 1. 前往tomcat官网复制下载链接, tomcat官网地址:https://tomcat.apache.org/ 2. 进入到指定目录,使用 wget 命令下 ...

  2. 老梗新玩「GitHub 热点速览 v.22.34」

    作者:HelloGitHub-小鱼干 不知道你是否和我有一样的烦恼,最近的流行梗当自己要用拿来造词时,就陷入了不知道咋"换壳"的尴尬地步.sao-gen-gen 大大减少了你老梗新 ...

  3. k8s-Pod基础

    制作镜像 第一个pod 搭建Harbor仓库 重启策略 启动命令 pod基本命令 设置环境变量 数据持久化和共享-hostPath 数据持久化和共享-emptyDir JSON格式编写pod文件 Co ...

  4. Flink介绍

    1,简介 Flink是Apache基金会旗下的一个开源大数据处理框架.Flink很牛逼,好多牛逼的公司都在用. 2,特征 *高吞吐和低延迟.每秒处理百万个时间,毫秒级延迟.有点既要老婆好,又要彩礼少的 ...

  5. Java-随机数据生成器(造数据)

    概述 简单易用的随机数据生成器.一般用于开发和测试阶段的数据填充.模拟.仿真研究.演示等场景.可以集成到各种类型的java项目中使用. 优点 非常轻量级(不到1M),容易集成,无需过多第三方依赖 简单 ...

  6. Springboot连接数据库 (解决报错)

    好家伙,来解决报错 1.新建项目时, 将SQL的" Spring Date 'jdbc' "点上 2.使用idea快速创建springboot项目时会出现连接不到服务器的情况 这里 ...

  7. Mysql_索引总结笔记

    Mysql 索引总结 1. 聚簇索引 InnoDB 引擎使用的就是聚簇索引,就是主键的索引,是一种数据的存储方式.所有的数据都是存储在索引的叶子结点上(与MySAM 引擎不同,MySAM是传统方式), ...

  8. 一个包搞定中文数据集: datasetstore

    工作中,总是要使用各种中文数据集,每次使用数据集都要花费不少的时间进行寻找,写预处理代码,结合不同的模型和框架做出相应的处理.有的时候好不容易找到合适的数据集,但是却因为网络问题,无法下载,下载了很长 ...

  9. 在Windows 2012 R2上安装vcenter 5.5

    在Windows 2012 R2上安装vCenter 5.5做个实验,发现安装的时候卡在Install Directory service了. 重启后,再装也一样. 上网查了一下,说是要装好ADLDS ...

  10. G&GH05 删除文件和.gitignore

    注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...