for 循环嵌套 for 循环,你需要懂的代码机能优化技能!

小微 科技for 循环嵌套 for 循环,你需要懂的代码机能优化技能!已关闭评论106字数 3428阅读模式
摘要前言本篇分析的技巧点其实是比较常见的,但是最近的几次的代码评审还是发现有不少兄弟没注意到。所以还是想拿出来说下。正文是个什么场景呢?就是 for循环 里面还有 for循环, 然后做...

本篇分析的技能点实际上是比较常见的,然而最近的几回的代码评审还是发现有很多兄弟没注意到。

所以还是想拿出来讲下。文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

正文

是个什么场景呢?文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

就是 for循环 里面还有 for循环, 然后做一些数据匹配、处理 这类场景。文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

咱们结合实例代码来看看。文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

场景示例:文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

比如咱们现在拿到两个list 数据 ,一个是 User List 聚拢 ;另外一个是 UserMemo List聚拢;文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

咱们需要遍历 User List ,然后依据 userId 从 UserMemo List 里面掏出 对应这个userId 的 content 值,做数据处理。文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

代码 User.java :文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

import lombok.Data;文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

@Data
public class User {
private Long userId;
private String name;
}

代码 UserMemo.java :文章源自微观生活(93wg.com)微观生活-https://93wg.com/4402.html

import lombok.Data;

@Data
public class UserMemo {
private Long userId;
private String content;
}

摹拟 数据聚拢 :

5W 条 user 数据 , 3W条 userMemo数据

public static List<User> getUserTestList {
List<User> users = new ArrayList<>;
for {
User user = new User;
user.setName.toString);
user.setUserId i);
users.add;
}
return users;
}

public static List<UserMemo> getUserMemoTestList {
List<UserMemo> userMemos = new ArrayList<>;
for {
UserMemo userMemo = new UserMemo;
userMemo.setContent.toString);
userMemo.setUserId i);
userMemos.add;
}
return userMemos;
}

先看平时大家不注意的时候可能会这样去写代码处理 :

ps:其实数据量小的话,其实没多大机能差别,无非咱们还是需要知道一些技能点。

代码:

public static void main {
List<User> userTestList = getUserTestList;
List<UserMemo> userMemoTestList = getUserMemoTestList;

StopWatch stopWatch = new StopWatch;
stopWatch.start;

for {
Long userId = user.getUserId;
for {
if )) {
String content = userMemo.getContent;
System.out.println;
}
}
}

stopWatch.stop;
System.out.println);

}

咱们来看看 这时候候的一个耗时情况 :

至关于迭代了 5W * 3W 次

可以看到历时 是 26857毫秒

其实到这,插入个题外点,如果说每一个userId 在 UserMemo List 里面 都是只有一条数据的场景。

for {
Long userId = user.getUserId;
for {
if )) {
String content = userMemo.getContent;
System.out.println;

}
}
}

单从这段代码有无问题 ,有无优化点。

显然是有的, 由于当咱们从内循环UserMemo List里面找到匹配数据的时候, 没有做其他操作了。

这样 内for循环会继续下,直到跑完再进行下一轮总体循环。

所以,仅针对这类情景,1对1的或者说咱们只需要找到一个匹配项,处理完后咱们 应当使用 break 。

咱们来看看 加之 break 的一个耗时情况 :

代码:

public static void main {
List<User> userTestList = getUserTestList;
List<UserMemo> userMemoTestList = getUserMemoTestList;

StopWatch stopWatch = new StopWatch;
stopWatch.start;

for {
Long userId = user.getUserId;
for {
if )) {
String content = userMemo.getContent;
System.out.println;
break;
}
}
}

stopWatch.stop;
System.out.println);

}

耗时情况:

可以看到 从 2W 多毫秒 变为了 1W 多毫秒, 这个break 加的很OK。

回到咱们刚才, 平时需要for 循环 里面再 for 循环 这类方式,可以看到耗时是 2万6千多毫秒。

那如果场景更繁杂一定, 是for 循环里面 for循环 多个或者, for循环里面还有一层for 循环 ,那这样代码耗时真的无比恐怖。

那么接下来这个技能点是 使用map 去优化 :

代码:

public static void main {
List<User> userTestList = getUserTestList;
List<UserMemo> userMemoTestList = getUserMemoTestList;

StopWatch stopWatch = new StopWatch;
stopWatch.start;

Map<Long, String> contentMap =
userMemoTestList.stream.collect);

for {
Long userId = user.getUserId;
String content = contentMap.get;

if ) {
System.out.println;
}

}

stopWatch.stop;
System.out.println);

}

看看耗时:

为何 这么显著的效果 ?

这其实就是时间繁杂度,for循环嵌套for循环,就好比 循环每一一个 user ,拿出 userId 需要在里面的循环从 userMemo list聚拢里面 按顺序去开盲盒匹配,拿出第一个,看看userId ,拿出第二个,看看userId ,一直找匹配的。

而咱们提早对 userMemo list聚拢 做一次 遍历,转存储在map里面 。

map的取值效力 在多数的情况下是能保持接近 O(1) 的 , 毕竟数据结构摆着,数组加链表。

至关于拿到userId 想去开盲盒的时候, 依据userId 这个key hash完能直接找到数组里面的索引标记位, 如果底下没链表(有的话O),直接掏出来就完事了。

然后补充一个getNode的代码注释 :

/**
* Implements Map.get and related methods.
* 这是个 Map.get 的实现 办法
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
// final 写死了 没法更改 返回 Node 传入查找的 hash 值 以及 key键
final Node<K,V> getNode {
// tab 还是 哈希表
// first 哈希表找的链表红黑树对应的 头结点
// e 代表当前节点
// k 代表当前的 key
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 赋值 并过滤 哈希表 空的长度不够的 对应位置没存数据的 都直接 return null
if != null && > 0 &&
& hash]) != null) {
// 头结点就 找到了 hash相等值相等 或者 不空的 key 以及当前节点 equals
if == key || )))
return first;
// 头结点不匹配 没找到就 就用 next 找
if != null) {
// 是否红黑树 的
if
return first).getTreeNode;
// 红黑树就直接 调用 红黑树内查找

// 不为空或者没找到就do while 循环
do {
// 当前节点 找到了 hash相等值相等 或者 不空的 key 以及当前节点 equals
if == key || )))
return e;
} while != null);
}
}
return null;
}

依照目前以JDK8 的hash算法,起hash冲突的情况是无比无比少见了。

最恶劣的情况,只有当 全体key 都冲突, 全都分配到一个桶里面去都占用一个位置 ,这时候候就是O(n),这类情形不需要去斟酌。

原文链接;https://mp.weixin.qq.com/s/Yo_k5B9j6nbK1_o3fn-6TA

以上就是微观生活(93wg.com)关于“for 循环嵌套 for 循环,你需要懂的代码机能优化技能!”的详细内容,希望对大家有所帮助!

继续阅读
 
小微
  • 版权声明: 本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们(管理员邮箱:81118366@qq.com),情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!
  • 转载请务必保留本文链接:https://93wg.com/4402.html