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<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

这是看得到的优化:

    @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循环放到外层,试试看:

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

代码示例:

    @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 :

    @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

 @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:

   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都不需要,不要自行车,不要摩托车,我们只要

最后

>>> 60/0.095
631.578947368421

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

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

java中如何将嵌套循环性能提高500倍的更多相关文章

  1. Java 中的静态嵌套类和非静态嵌套类

    Java 中的静态嵌套类和非静态嵌套类 术语:嵌套类分为两类:静态嵌套类和非静态嵌套类.声明 static 的嵌套类称为静态嵌套类,非静态嵌套类也称为内部类. class OuterClass { p ...

  2. JAVA中使用HTTP 1.1提高基于AXIS 1.4的web service的性能

    HTTP 1.1会在第一次连接的时候进行认证, 而在一定时间内保持连接而不用重新验证. 一般情形下,每个web service请求都会在web service服务端验证, 而验证会消耗很多时间, 因此 ...

  3. java中遍历MAP,嵌套map的几种方法

    java中遍历MAP的几种方法 Map<String,String> map=new HashMap<String,String>();    map.put("us ...

  4. Java中集合的嵌套

    集合的嵌套遍历 获取10个1-20之间的随机数,要求不能重复 键盘录入多个数据,以0结束,要求在控制台输出这多个数据的最大值. public static void main(String[] arg ...

  5. PayPal为什么从Java迁移到Node.js 性能提高一倍 文件代码减少44%

    大家都知道PayPal是另一家迁移到Node.js平台的大型公司,Jeff Harrell的这篇博文 Node.js at PayPal  解释了为什么从Java迁移出来的原因: 开发效率提高一倍(2 ...

  6. Java中的Checked Exception——美丽世界中潜藏的恶魔?

    在使用Java编写应用的时候,我们常常需要通过第三方类库来帮助我们完成所需要的功能.有时候这些类库所提供的很多API都通过throws声明了它们所可能抛出的异常.但是在查看这些API的文档时,我们却没 ...

  7. 转载:Java中的Checked Exception——美丽世界中潜藏的恶魔?

    转自 Amber-Garden 的 博客 https://www.cnblogs.com/loveis715/p/4596551.html 在使用Java编写应用的时候,我们常常需要通过第三方类库来帮 ...

  8. java中的条件语句(if、if...else、多重if、嵌套if)

    Java条件语句之 if 生活中,我们经常需要先做判断,然后才决定是否要做某件事情.例如,如果考试成绩大于 90 分,则奖励一个 IPHONE 5S .对于这种"需要先判断条件,条件满足后才 ...

  9. 【Java入门提高篇】Day14 Java中的泛型初探

    泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解: 1.什么是泛型. 2.如何使用泛型. 3.泛型的好处. 1.什么是泛型? 泛型,字面意思便是参数化类型,平时 ...

随机推荐

  1. C++ DLL注入工具完整源码

    先上源码 #include "Inject_Main.h" #include "resource.h" #include <Windows.h> # ...

  2. mpfu 位编辑处理?

    1.  国内的不用处理,其余都做 2.  判断是否是mp   的项目 3  设置位数编辑    请求中    和  检查中都要做 4  以某一画面作为基准,修改不同的值. 5  对象外的数据直接设置在 ...

  3. vivo直播应用技术实践与探索

    一.概述 2019年vivo直播平台立项,初期与优秀的顶部直播平台进行联运直播开发,进行市场,产品和技术的初步探索:再到后来为了丰富直播的内容和形式,开始自己独立探索:之后,我们结合vivo现阶段的直 ...

  4. 「IOI2009」旅行商

    题目传送门 首先,看到这道题感觉就像dp(然鹅没什么用). 一个美好的设想 假如没有两个展销会在同一天开展:前途光明 暴力dp,复杂度o(\(n^2\)). 没有同一天的展销会 暴力dp慢,是因为本质 ...

  5. AT [ABC176F] Brave CHAIN

    首先可以发现这样一个事实:在每次操作当中,都有三张牌是已经固定的,只有两张牌是不确定的,于是我们可以发下每一次操作的状态可以简单的使用这两张牌来描述,于是可以考虑令 \(dp_{i, j, k}\) ...

  6. php栈的定义及入栈出栈的实现 算法

    转自:php栈的定义及入栈出栈的实现 栈是线性表的一种,他的特点是后入先出,可以这么理解,栈就像一个存东西的盒子,先放进去的在最底层,后放进去的在上层,因为上层的东西把底层的东西压住了,下层的想要出去 ...

  7. CephFS分布式文件系统

    目录 组件 基本组件 块存储 文件存储 对象存储 特点: 1.高性能: 2.高可用性: 3.高可扩展性: 4.特性丰富: 详细配置 一.准备机器 1.修改主机名 2.修改hosts文件 二.Ceph节 ...

  8. Lesson12——NumPy 字符串函数之 Part1:字符串操作函数

    NumPy 教程目录 1 NumPy 字符串函数 以下函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作. 它们基于 Python 内 ...

  9. 生产环境搭建高可用Harbor(包括恢复演练实操)

    生产环境搭建高可用Harbor(包括恢复演练实操) 前言 因资源成本问题,本Harbor高可用架构为最小开销方案,如果资源充足,可以将PG.Redis全部使用使用云厂商集群模式. 同时为了配置简单,并 ...

  10. JavaScript 的DOM操作详解

    内容概要 DOM之查找标签 基本查找 间接查找 节点操作 获取值操作 class操作 样式操作 事件 内置参数this 事件练习 内容详细 DOM操作 DOM(Document Object Mode ...