java中如何将嵌套循环性能提高500倍
java中如何将嵌套循环性能提高500倍
转载请注明出处https://www.cnblogs.com/funnyzpc/p/15975882.html
前面
似乎上一次更新在遥远的九月份,按照既定的时间线应该要补5篇博文才对得起这懒惰的半年, 近期工作强度虽不大,但也时有烦心的事儿,比如这忽冷忽热的天气、反反复复的疫情、不大不小的房贷、还有我那半死不活的手机,当然咯,手机这月必须得换了,准备xperia 5 Ⅲ或者iPhone SE ,资金若是充裕的话也给老爸换一部(耳机也安排上),各位觉得如何呢;哈哈,扯远了,现在就来填一下坑(补一篇博客)。
首先,我面对的问题是:两拨数据都从db抽取到应用(主要是mysql的AP能力太感人了),在应用里面做嵌套循环处理的时候发现十分的缓慢,看到cnblogs的网友有做优化,遂就顺带就学了一手,似乎是好了许多,但是对于极致性能追求的我怎能就这样马马虎虎地过呢。。。oh不能!!!
现在开始: show me code ~
代码及基本业务逻辑
我们是从db抽出两拨数据,两拨数据需要做匹配同时还要配合着配置项计算相关的金额,计算金额无非就是BigDecimal嘛,这里略去哈~ ...下面我就demo出两拨测试数据及最原始的代码逻辑,很简陋哈~
oh,对了,我电脑配置为8核16GB 256SSD => MacBook Pro ,所以各位电脑运行效率有差异很正常哈
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 lst_5w = this.build5W();
List lst_60w = this.build60W();
Set count = new HashSet<>(lst_5w.size());
long s = Instant.now().toEpochMilli();
for(int i = 0;
i build60W(){
List lst = new ArrayList<>(600000);
SHOT_STATIC_IT.getAndSet(100000);
for(int i=0;
i<600000;
i++){
lst.add( genSeq());
}
return lst;
}// 5万数据
private List build5W(){
List 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();
}}
【java中如何将嵌套循环性能提高500倍】
整体耗时
60.318秒 64.304秒`以上
test00
部分即为业务逻辑,不用笑话,写的确实很low哈哈,主要就是比较两拨数据匹配到的打印出来,同时这个数要能被2整除才行~ ,当然接下来的优化主要针对test00进行优化哈~第一波是看得到的优化::去掉不必要的冗余循环+在需要的时候果断break
这是看得到的优化:
@Test
public void test01(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
Set count = new HashSet<>(lst_5w.size());
long s = Instant.now().toEpochMilli();
for(int i = 0;
i
来,看看效率如何->
9.958秒 10.123秒
(为两次执行结果)wow,太棒了,我们得到了6x左右的优化,赞
试想一下,如果我们做一个功能,调用一次,用户需要等待10s,这样合适嘛?,再试试看~
第二波优化::来自博客网友的助攻->
内大外小
这里主要方式是将大list放到内层,小list循环放到外层,试试看:public void test02(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
Set count = new HashSet<>(lst_5w.size());
long s = Instant.now().toEpochMilli();
for(int j = 0;
j
执行时间为=>
6.314秒 6.306秒
(两次执行结果)相对于前一次,我们得到了40%的优化,看起来也不错,只是还需要等6s+, 小小的一步。。。听网友说,他们还有其他方案,再试试看~
第三波优化:for循环参数提出循环内+循环参数常量化
final
代码示例:@Test
public void test03(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
Set 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
oh,似乎没有明显的优化,而且执行效率也降低了许多哦=>
7.382秒 6.376秒
(两次执行结果)ennnn....,java提供的循环方式多种,病急的时候我们会乱投医,尤为盲目的时候。。。
第四波优化:使用for增强方式=>
for :
@Test
public void test04(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
Set 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
@Test
public void test05(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
Set 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
执行效率=>
2.61s 2.44s
(两次执行结果)难以置信,它相比以上 整整提高了1倍的效率,当你以为在多线程下洋洋得意的时候,以为它只能在
2.5s
左右徘徊嘛???NO NO NO。。。。?????? 第六波优化::终极优化之=>
HashMap
我想,很多使用java多年的同学都很难想到此,其实一开始我也不知道,只是一个偶然的时间瞟了一眼HashMap的源码 从此发现了天机。。。final code:
public void test06(){
List lst_5w = this.build5W();
List lst_60w = this.build60W();
final Integer value = https://www.it610.com/article/1;
Set count = new HashSet<>(lst_5w.size());
HashMap 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都不需要,不要自行车,不要摩托车,我们只要
最后
>>> 60/0.095
631.578947368421
500x
的效率提升,标题着实有点儿保守了,各位不妨在各自电脑上试试看,当然如果您有其他优化思路 麻烦也告知下哈(建设性的更好)现在是 2022-03-07 21:50 各位晚安
推荐阅读
- Java序列化流的奇妙之旅
- 前端|解决 JavaScript heap out of memory 报错
- Java常见问题
- Java Instrument
- java|编程的智慧(转自王垠个人博客)——满满的编程实践经验不看就错过
- 程序员|一份字节跳动面试官给你的Java技术面试指南,跳槽薪资翻倍
- Linux内核|LinuxKernel内核百炼成神之渡劫中断【贰】
- 程序员|java高分面试指南(淘宝java框架)
- JavaScript|十进制数转二进制
- Java中实现List分隔成子List详解