三层for循环,搁这玩俄罗斯套娃呢?

440 阅读2分钟

参考文章

最近项目有个场景需要使用上三个互有关系的集合,当时脑子一抽直接整了个三层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;
}

看结果

image.png 从上图看出,基于0~10W的数据量,采用for循环的执行时间随着数据量变大而变长(我真的没有摸鱼),而采用HashMap结构优化后,执行几乎在瞬间完成。

总结

优化方法

  1. 找出定位到两组数据的某个属性,例如这里就是DtoA的var1与DtoB的var2相等时或DtoB的var1与DtoC的var2相等时。
  2. 取后者属性作为key,后者对象本身作为value,构建Map,注意保证键的唯一性
  3. 以前者的属性作为key,即可直接从Map中获取需要操作的对象。 以后再碰到循环处理三组数据,甚至数据量稍大点的两组数据的场景,再也不用担心被人骂套娃了。