策略与优化 - 001- 在特定场景下,如何对双层循环进行降级,加速数据匹配?

Posted selfcoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了策略与优化 - 001- 在特定场景下,如何对双层循环进行降级,加速数据匹配?相关的知识,希望对你有一定的参考价值。

一、场景介绍

假设某次搜索结果中有 100_0000 篇文章,而你的个人收藏中有 10000 篇,如何在短时间内快速识别 100_0000 中哪些是 “已收藏”, 哪些是 “未收藏” ?

 

二、正常逻辑(双层for 循环)

public class ForEachTest {
    public static void main(String[] args) {
        // user book list
        List<String> ubList = new ArrayList<>();
        
        // book list
        List<String> bList = new ArrayList<>();
        
        // 收藏的数据条数
        int collectionNum = 10000;
        // 总条数
        int total = 100_0000;
        
        // 预计要存储的结果,也可以定义在要返回到页面实体的状态中
        Map<String, Integer> resMap = new HashMap<>(total);
        
        // 初始化 个人中心收藏的数据
        for (int i = 0; i < collectionNum; i++) {
            ubList.add(String.valueOf(i));
        }
        
        // 初始化 搜索结果中返回的数据,同时维护一个 个人与文章 相关的状态map
        // 状态初始结果为 0:“未收藏”, 1:“已收藏”
        for (int i = 0; i < total; i++) {
            bList.add(String.valueOf(i));
            
            //如果这个 bList 中存储的是 实体对象,则可以在存入数据的时候,就初始化一个未收藏的状态
            resMap.put(String.valueOf(i), 0);
        }

        // 记录开始时间
        long start = System.currentTimeMillis();

        /* for 双层循环*/
        for (String b : bList) {
            for (String ub : ubList) {
                if (b.equals(ub)) {
                    resMap.put(b, 1);
                }
            }
        }
        
        // 结束时间
        long end = System.currentTimeMillis();
        // 用时时长 ms
        System.out.println("耗时 ms: " + (end - start));
    }
}

 

注: 在这几次的测试中,都没有涉及内存的消耗与数据准备的时间,具体查看时间计算的区间!

测试数据:

  collectionNum: 10000

  total : 100 0000

  消耗时间:

       技术分享图片

结论:双层 for 循环,遍历了 100亿次,用时 62秒 左右

 

三、利用 HashMap 底层,减少无效的遍历

public class ForEachTest {
    public static void main(String[] args) {
        // user book list
        List<String> ubList = new ArrayList<>();
        
        // book list
        List<String> bList = new ArrayList<>();
        
        // 收藏的数据条数
        int collectionNum = 10000;
        // 总条数
        int total = 100_0000;
        
        // 预计要存储的结果,也可以定义在要返回到页面实体的状态中
        Map<String, Integer> resMap = new HashMap<>(total);
        
        // 初始化 个人中心收藏的数据
        for (int i = 0; i < collectionNum; i++) {
            ubList.add(String.valueOf(i));
        }
        
        // 初始化 搜索结果中返回的数据,同时维护一个 个人与文章 相关的状态map
        // 状态初始结果为 0:“未收藏”, 1:“已收藏”
        for (int i = 0; i < total; i++) {
            bList.add(String.valueOf(i));
            
            //如果这个 bList 中存储的是 实体对象,则可以在存入数据的时候,就初始化一个未收藏的状态
            resMap.put(String.valueOf(i), 0);
        }

        // 记录开始时间
        long start = System.currentTimeMillis();

        // 将 个人中心收藏的数据,转化存储到 map 中,
        // 注意 收藏的文章的id 作为key,value 随意,这里使用同样使用了 id
        Map<String, String> ubMap = new HashMap<>();
        for (String ubId : ubList) {
            ubMap.put(ubId, ubId);
        }

        // 开始遍历 搜索结果中的 100万条数据,是否有被个人收藏过的,有就改变返回的状态。
        for (String bId : bList) {
            // 直接使用 ubMap 的查找key 值是否存在的方式,判断该文章是否已经收藏。
            if (ubMap.containsKey(bId)){
                resMap.put(bId, 1);
            }
        }
        
        // 结束时间
        long end = System.currentTimeMillis();
        // 用时时长 ms
        System.out.println("耗时 ms: " + (end - start));
    }
}

 

 

测试数据:

  collectionNum: 10000

  total : 100 0000

  消耗时间:

       技术分享图片

 

结论:一层循环,加上内部的 hash 计算,用时 51ms

 

四、总结

条件: 两组数据分别没有重复的数据(id 或者 根据对比的字段不重复,也可以根据业务琢磨,即放在 map 中的 key 值不重复)

需求:对比一组数据中的数据,是否在另一组中有对应的匹配数据

结论使用 hashmap 的 key值 进行查找,明显快于双层 for 循环,for 循环消耗的时间是 key 值查找的 100多倍!!!

 

五、原理解析

1. 双层 for 循环就不需要多解释,纯粹的 10000 x 100 00000 = 100 亿 的遍历次数

2. HashMap 快的原因在于将 id 值作为 map 的key存储在map中,而 map 底层是 数组在存储数据,此处不对链表和树结构进行说明。通过计算 id 的 hashcode 值,再与 map 的容量 size 求余数,直接获取到该条数据在 hashMap 中的下标,而不是逐一的去查找数据。故 使用 hashmap 只循环了一次  + 少量运算,速度明显有所突破。

3. 根据需要控制内存消耗大小,你可以自定义将 数据多的放在 map 或者将数据少的放在 map 中,也就是在控制外层循环的次数,外层大,则占用内存就小,时间上可能会有所增加。

 

以上是关于策略与优化 - 001- 在特定场景下,如何对双层循环进行降级,加速数据匹配?的主要内容,如果未能解决你的问题,请参考以下文章

22 六种常见SQL场景及其在TDH中的优化策略

论使用HashMap优化双层For循环的实际性能

特定场景下SQL的优化

BOBCAT: 基于双层优化的计算机化自适应测验(超硬核解读论文+阅读论文技巧)

如何优化读写频繁的数据库

[AWS][安全] S3存储桶策略-Bucket Policy