最近项目有个场景需要使用上三个互有关系的集合,当时脑子一抽直接整了个三层for循环出来,同事路过时来了个惊鸿一瞥:“搁这玩俄罗斯套娃呢?你知道这三层for循环的时间复杂度是多少吗?你咋不用HashMap处理下呢?”,被他这么一说,我碾都红了,马上就去查怎么优化这三层for循环。
项目代码不方便贴出,这里的代码是自己整理的示例。
上代码
主测试代码
package com.bandao.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Demo {
public static void main(String[] args) {
for (int i = 0; i <= 100000; i += 1000) {
List<DtoA> list1 = getList1(i);
List<DtoB> list2 = getList2(i);
List<DtoC> list3 = getList3(i);
long timeBy3For = getTimeBy3For(list1, list2, list3);
long timeByHashMap = getTimeByHashMap(list1, list2, list3);
System.out.println(i + "," + timeBy3For + "," + timeByHashMap);
}
}
private static long getTimeBy3For(List<DtoA> list1, List<DtoB> list2, List<DtoC> list3) {
long start = System.currentTimeMillis();
for (DtoA a : list1) {
for (DtoB b : list2) {
if (a.getVar1().equals(b.getVar2())) {
for (DtoC c : list3) {
if (b.getVar1().equals(c.getVar2())) {
// 业务操作
break;
}
}
break;
}
}
}
long end = System.currentTimeMillis();
return end - start;
}
private static long getTimeByHashMap(List<DtoA> list1, List<DtoB> list2, List<DtoC> list3) {
long start = System.currentTimeMillis();
Map<String, DtoB> list2Map = list2.stream().collect(Collectors.toMap(DtoB::getVar2, Function.identity()));
Map<String, DtoC> list3Map = list3.stream().collect(Collectors.toMap(DtoC::getVar2, Function.identity()));
for (DtoA a : list1) {
DtoB b = list2Map.get(a.getVar1());
if (null == b) {
continue;
}
DtoC c = list3Map.get(b.getVar1());
if (null == c) {
continue;
}
if (b.getVar1().equals(c.getVar2())) {
// 业务操作
continue;
}
}
long end = System.currentTimeMillis();
return end - start;
}
private static List<DtoA> getList1(int size) {
List<DtoA> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(new DtoA(String.valueOf(i), String.valueOf(size - i)));
}
return list;
}
private static List<DtoB> getList2(int size) {
List<DtoB> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(new DtoB(String.valueOf(size - i), String.valueOf(i)));
}
return list;
}
private static List<DtoC> getList3(int size) {
List<DtoC> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(new DtoC(String.valueOf(i), String.valueOf(size - i)));
}
return list;
}
}
三个基础dto代码
package com.bandao.demo;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class DtoA {
private String var1;
private String var2;
}
package com.bandao.demo;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class DtoB {
private String var1;
private String var2;
}
package com.bandao.demo;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class DtoC {
private String var1;
private String var2;
}
看结果
从上图看出,基于0~10W的数据量,采用for循环的执行时间随着数据量变大而变长(我真的没有摸鱼),而采用HashMap结构优化后,执行几乎在瞬间完成。
总结
优化方法:
- 找出定位到两组数据的某个属性,例如这里就是DtoA的var1与DtoB的var2相等时或DtoB的var1与DtoC的var2相等时。
- 取后者属性作为key,后者对象本身作为value,构建Map,注意保证键的唯一性。
- 以前者的属性作为key,即可直接从Map中获取需要操作的对象。 以后再碰到循环处理三组数据,甚至数据量稍大点的两组数据的场景,再也不用担心被人骂套娃了。