前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java中如何将嵌套循环性能提高500倍

java中如何将嵌套循环性能提高500倍

作者头像
上帝
发布2022-05-10 21:06:52
5540
发布2022-05-10 21:06:52
举报
文章被收录于专栏:影子影子

java中如何将嵌套循环性能提高500倍

转载请注明出处https://cloud.tencent.com/developer/article/1999836

前面

似乎上一次更新在遥远的九月份,按照既定的时间线应该要补5篇博文才对得起这懒惰的半年😑, 近期工作强度虽不大,但也时有烦心的事儿,比如这忽冷忽热的天气、反反复复的疫情、不大不小的房贷、还有我那半死不活的手机,当然咯,手机这月必须得换了,准备xperia 5 Ⅲ或者iPhone SE ,资金若是充裕的话也给老爸换一部(耳机也安排上),各位觉得如何呢;哈哈😄,扯远了,现在就来填一下坑(补一篇博客)。

首先,我面对的问题是:两拨数据都从db抽取到应用(主要是mysql的AP能力太感人了),在应用里面做嵌套循环处理的时候发现十分的缓慢,看到cnblogs的网友有做优化,遂就顺带就学了一手,似乎是好了许多,但是对于极致性能追求的我怎能就这样马马虎虎地过呢。。。oh不能!!!

现在开始: show me code ~😜

代码及基本业务逻辑

我们是从db抽出两拨数据,两拨数据需要做匹配同时还要配合着配置项计算相关的金额,计算金额无非就是BigDecimal嘛,这里略去哈~ ...下面我就demo出两拨测试数据及最原始的代码逻辑,很简陋哈~😂

oh,对了,我电脑配置为8核16GB 256SSD => MacBook Pro ,所以各位电脑运行效率有差异很正常哈😉

代码语言:javascript
复制
package com.mee.base;

import cn.hutool.core.collection.ConcurrentHashSet;
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class BigDataLoopTest {

    // 简单的业务逻辑代码
    @Test
    public void test00(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        Set<Integer> count = new HashSet<>(lst_5w.size());
        long s = Instant.now().toEpochMilli();
        for(int i = 0;i<lst_60w.size();i++){
            for(int j = 0;j<lst_5w.size();j++){
                Integer val = lst_5w.get(j);
                if(val%2==0 && lst_60w.get(i).equals(val)){
                    count.add(val);
                    System.out.println(val);
                    // 这里加不加break似乎性能相差无几~
//                    break;
                }
            }
        }
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

   //  60万数据
    private List<Integer> build60W(){
        List<Integer> lst = new ArrayList<>(600000);
        SHOT_STATIC_IT.getAndSet(100000);
        for(int i=0;i<600000;i++){
            lst.add( genSeq());
        }
        return lst;
    }

   // 5万数据
    private List<Integer> build5W(){
        List<Integer> lst = new ArrayList<>(100000);
        SHOT_STATIC_IT.getAndSet(1);
        for(int i=100000;i<600000;i++){
            int val = genSeq();
            if(val%7==0){
                lst.add(val);
            }
            if(lst.size()==50000){
                return lst;
            }
        }
        return lst;
    }

    // 构造数
    private static final AtomicInteger SHOT_STATIC_IT = new AtomicInteger(1);
    public static int genSeq(){
        if(SHOT_STATIC_IT.intValue()>990000){
            SHOT_STATIC_IT.getAndSet(1);
        }
        return SHOT_STATIC_IT.getAndIncrement();
    }

}

整体耗时 60.318秒 64.304秒`

以上test00部分即为业务逻辑,不用笑话,写的确实很low哈哈,主要就是比较两拨数据匹配到的打印出来,同时这个数要能被2整除才行~ ,当然接下来的优化主要针对test00进行优化哈~😊

第一波是看得到的优化::去掉不必要的冗余循环+在需要的时候果断break

这是看得到的优化:

代码语言:javascript
复制
    @Test
  public void test01(){
      List<Integer> lst_5w = this.build5W();
      List<Integer> lst_60w = this.build60W();
      Set<Integer> count = new HashSet<>(lst_5w.size());
      long s = Instant.now().toEpochMilli();
      for(int i = 0;i<lst_60w.size();i++){
          Integer val = lst_60w.get(i);
          if(val%2 == 0){
              for(int j = 0;j<lst_5w.size();j++){
                  Integer val2 = lst_5w.get(j);
                  if(val.equals(val2)){
                      count.add(val);
                      System.out.println(val2);
                      break;
                  }
              }
          }
      }
      System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
  }

来,看看效率如何->9.958秒 10.123秒 (为两次执行结果)

wow,太棒了,我们得到了6x左右的优化,赞👍

试想一下,如果我们做一个功能,调用一次,用户需要等待10s,这样合适嘛🙁️,再试试看~

第二波优化::来自博客网友的助攻->内大外小

这里主要方式是将大list放到内层,小list循环放到外层,试试看:

代码语言:javascript
复制
public void test02(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        Set<Integer> count = new HashSet<>(lst_5w.size());
        long s = Instant.now().toEpochMilli();
        for(int j = 0;j<lst_5w.size();j++){
            Integer val = lst_5w.get(j);
            if(val % 2 == 0) {
                for (int i = 0; i < lst_60w.size(); i++) {
                    if (lst_60w.get(i).equals(val)) {
                        count.add(val);
                        System.out.println(val);
                        break;
                    }
                }
            }
        }
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

执行时间为=>6.314秒 6.306秒(两次执行结果)

相对于前一次,我们得到了40%的优化,看起来也不错,只是还需要等6s+, 小小的一步。。。听网友说,他们还有其他方案,再试试看~

第三波优化:for循环参数提出循环内+循环参数常量化final

代码示例:

代码语言:javascript
复制
    @Test
    public void test03(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        Set<Integer> count = new HashSet<>(lst_5w.size());
        long s = Instant.now().toEpochMilli();
        int j;
        final int j_len = lst_5w.size();
        int i;
        final int i_len = lst_60w.size();
        for(j = 0;j<j_len;j++){
            Integer val = lst_5w.get(j);
            if(val % 2 == 0){
                for(i = 0;i<i_len;i++) {
                    if (lst_60w.get(i).equals(val)) {
                        count.add(val);
                        System.out.println(val);
                        break;
                    }
                }
            }
        }
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

oh,似乎没有明显的优化,而且执行效率也降低了许多哦😯=> 7.382秒 6.376秒(两次执行结果)

ennnn....,java提供的循环方式多种,病急的时候我们会乱投医,尤为盲目的时候。。。

第四波优化:使用for增强方式=>for :

代码语言:javascript
复制
    @Test
    public void test04(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        Set<Integer> count = new HashSet<>(lst_5w.size());
        long s = Instant.now().toEpochMilli();
        int i;
        final int i_len = lst_60w.size();
        for(Integer val:lst_5w){
            if(val % 2 == 0) {
                for (i = 0; i < i_len; i++) {
                    if (lst_60w.get(i).equals(val)) {
                        count.add(val);
                        System.out.println(val);
                        break;
                    }
                }
            }
        }
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

它似乎只回到了初次优化的效率=> 6.323秒 6.342秒(两次执行结果) ;此时,我们遗忘了很久的工具它似乎带来了一线光明 😇

第五波优化:并行流多线程=>parallelStream

代码语言:javascript
复制
 @Test
    public void test05(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        Set<Integer> count = new ConcurrentHashSet<>(lst_5w.size());
        long s = Instant.now().toEpochMilli();
        final int i_len = lst_60w.size();
        lst_5w.parallelStream().forEach(val->{
            if(val % 2 == 0){
                for(int i = 0;i<i_len;i++) {
                    if (lst_60w.get(i).equals(val)) {
                        count.add(val);
                        System.out.println(val);
                        break;
                    }
                }
//                for(Integer val2:lst_60w){
//                    if (val2.equals(val)) {
//                        System.out.println(val);
//                        break;
//                    }
//                }
            }
        });
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

执行效率=> 2.61s 2.44s (两次执行结果)

难以置信,它相比以上 整整提高了1倍的效率,当你以为在多线程下洋洋得意的时候,以为它只能在2.5s左右徘徊嘛???

NO NO NO。。。。☝️☝️☝️

第六波优化::终极优化之=>HashMap

我想,很多使用java多年的同学都很难想到此,其实一开始我也不知道😂😂😂,只是一个偶然的时间瞟了一眼HashMap的源码 从此发现了天机。。。😈

final code:

代码语言:javascript
复制
   public void test06(){
        List<Integer> lst_5w = this.build5W();
        List<Integer> lst_60w = this.build60W();
        final Integer value = 1;
        Set<Integer> count = new HashSet<>(lst_5w.size());
        HashMap<Integer,Integer> map_60w = new HashMap<>(lst_60w.size(),1);
        for(Integer key:lst_60w){
            map_60w.put(key,value);
        }
        long s = Instant.now().toEpochMilli();
        for(Integer val:lst_5w){
            if(val % 2 == 0) {
                Integer val2 = map_60w.get(val);
                if (null!=val2 /*&& val2.equals(val)*/) {
                    count.add(val);
                    System.out.println(val);
                    continue;
//                    break;
                }
            }
        }
        System.out.println("匹配到个数"+count.size()+" 耗时"+(Instant.now().toEpochMilli()-s)/1000D+"秒");
    }

oh,天。。。它只需要=>0.082秒 0.099秒 0.095秒 (三次执行结果)

我只是试试看的心态,结果着实震撼到我了...0.1s都不需要,不要自行车,不要摩托车,我们只要🚀

最后

代码语言:javascript
复制
>>> 60/0.095
631.578947368421

500x的效率提升,标题着实有点儿保守了,各位不妨在各自电脑上试试看,当然如果您有其他优化思路 麻烦也告知下哈(建设性的更好)😆😆😆

现在是 2022-03-07 21:50 各位晚安😴

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java中如何将嵌套循环性能提高500倍
    • 前面
      • 代码及基本业务逻辑
      • 第一波是看得到的优化::去掉不必要的冗余循环+在需要的时候果断break
    • 第二波优化::来自博客网友的助攻->内大外小
      • 第三波优化:for循环参数提出循环内+循环参数常量化final
        • 第四波优化:使用for增强方式=>for :
          • 第五波优化:并行流多线程=>parallelStream
          • NO NO NO。。。。☝️☝️☝️
            • 第六波优化::终极优化之=>HashMap
              • 最后
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档