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

本文探讨了一种常见问题:如何在大量数据中快速识别已收藏和未收藏的项。通过对比传统的双层循环方法与使用HashMap的优化方案,展示了使用HashMap的高效性,可以将100亿次遍历降低到51毫秒,优化了近100倍。
摘要由CSDN通过智能技术生成

一、场景介绍

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

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

public class foreachtest {

public static void main(string[] args) {

// user book list

list ublist = new arraylist<>();

// book list

list blist = new arraylist<>();

// 收藏的数据条数

int collectionnum = 10000;

// 总条数

int total = 100_0000;

// 预计要存储的结果,也可以定义在要返回到页面实体的状态中

map 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

消耗时间:

28e01e743f02122316dfa40d98e85cd7.png

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

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

public class foreachtest {

public static void main(string[] args) {

// user book list

list ublist = new arraylist<>();

// book list

list blist = new arraylist<>();

// 收藏的数据条数

int collectionnum = 10000;

// 总条数

int total = 100_0000;

// 预计要存储的结果,也可以定义在要返回到页面实体的状态中

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

消耗时间:

3ee666c4ff1c1b95d033f893f150a284.png

结论:一层循环,加上内部的 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 中,也就是在控制外层循环的次数,外层大,则占用内存就小,时间上可能会有所增加。

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值