首页 > 其他分享 >还在用双层for循环吗?太慢了

还在用双层for循环吗?太慢了

时间:2022-11-03 12:12:50浏览次数:50  
标签:双层 Person List list1 list2 毫秒 循环 太慢

前情提要

我们在开发中经常碰到这样的场景,查出两个 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 循环

标签:双层,Person,List,list1,list2,毫秒,循环,太慢
From: https://www.cnblogs.com/javammc/p/16854029.html

相关文章

  • 控制循环
    目录控制循环选择循环的种类循环的灵活度和检查位置什么时候使用while循环什么时候用带退出的循环循环控制前言进入循环控制循环使用循环是编程中最复杂的方面之一:知道......
  • 循环~倒数字三角形
    题目描述数字三角形的特征是三角形由数字填满,每行数字从1到9再到0周而复始挨个排列。你的任务是根据整数值输出倒数字三角形。输入输入数据含有一些整数,其值在[1,40]之......
  • 循环~画高楼
    题目描述小明想拥有一栋有烟囱的房子。 请写程序从键盘输入n(限定n为小于10的正整数),然后参照样例,用"*"画出一栋n层的楼房。 不管有几层,楼房的屋顶和烟囱都是一样的。 ......
  • 求和及求平均-while循环语句的应用-2022-11-2
    packagescanner;importjava.util.Scanner;publicclassDemo04{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);......
  • Linux 循环,格式以及使用方法
    ​for循环:(每读取一行在字符串下面添加#号)[root@localhost]#cat/etc/passwd|awk-F':''{print$3}'|tail-5>test1.txt[root@localhost]#cattest1.txt98972......
  • React循环DOM时为什么需要添加key
    一、React渲染流程和更新流程react渲染流程:jsx->虚拟dom->真实domreact更新流程:props/state改变->render函数重新执行->生成新的虚拟dom树->新旧虚拟dom树进......
  • Java学习笔记day2--循环结构
    **循环结构包括四个部分:初始化部分循环条件部分循环体部分迭代部分1>for循环for(初始化部分;循环条件部分;迭代条件......
  • Java学习笔记day2——循环结构综合例题(1个)
    //循环语句综合例题/*例题1:从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为零时,结束程序。*/......
  • Python—循环遍历
    一、循环遍历遍历某个结构形成的循环运行方式for<循环变量>in<遍历结构>:<语句块>从遍历结构中逐一提取元素,放在循环变量中:由保留字for和in组成,完整遍历所......
  • 四、循环语句plus
    一.while循环的基础语法语法while条件语句:条件满足,做事情1条件满足,做事情2条件满足,做事情3(省略)...注意事项条件语句结果:布尔类型【True:继续循......