如何优化两个For循环的嵌套查询?
我们先来看看业务场景,我做了一个小模拟:
首先有十万条学生的信息,以及三万条老师的信息。老师与学生都有两个 属性,一个是token 一个是 id。
现在业务如下 我需要将拥有相同id的老师的token赋值给学生
eg: 老师id=1 学生id=1 那么就把老师token给学生
我们先看一下学生和老师的类:
//这里用了lombok 没有的请自己生成get和set还有构造函数
@Data
@AllArgsConstructor
public class Student {
private String token;
private Integer id;
}
//老师的
@Data
@AllArgsConstructor
public class Teacher {
private String token;
private Integer id;
}
然后我们来赋值:
public static void main(String[] args) {
long stime = System.currentTimeMillis();
List<Student> students = new ArrayList<Student>();
List<Teacher> teachers = new ArrayList<Teacher>();
// 业务场景如下 首先有十万条学生的信息,以及三万条老师的信息。
// 老师与学生都有两个 属性 一个是token 一个是 id
// 现在模拟赋值 将i的值赋给id token的值由UUID生成
for(int i = 0; i < 100000; i++){
Student student = new Student(UUID.randomUUID().toString(),i);
students.add(student);
}
for(int i = 0; i < 30000; i++){
Teacher teacher = new Teacher(UUID.randomUUID().toString(),i);
teachers.add(teacher);
}
//到此为止 执行时间为:278 毫秒
}
现在业务如下 我需要将拥有相同id的老师的token赋值给学生
eg:老师id=1 学生id=1 那么就把老师token给学生
那么如何实现上述业务呢? 我们总共有三个方法 。先看方法一:
方法一:两个For循环,然后查找
for (Student student : students) {
for (Teacher teacher : teachers) {
if(teacher.getId().equals(student.getId())){
student.setToken(teacher.getToken());
//执行时长:24401 毫秒
}
}
}
哦吼,执行时常将近25s,这能忍吗?这不能忍!!!!
方法二:返回Break(!此方法适用于 仅有一个相同的 例如不能有两个学生的id都与老师相同)
/**
* 增加break
* 此方法适用于 仅有一个相同的 例如不能有两个学生的id都与老师相同
*/
for (Student student : students) {
for (Teacher teacher : teachers) {
if(teacher.getId().equals(student.getId())){
student.setToken(teacher.getToken());
break;
//执行时长:7580 毫秒.
}
}
}
emmmm,七秒钟,优化了将近三倍,但是还是接受不了,区区从十万条数据中查找三万条就花了七秒,那百万级别的还不得宕机啊,还是不能忍。
/**
* 使用map
* 可以把需要操作的list转化为map
* 需要操作:我们需要把 老师的id作为键 把token作为值
* 然后我们以学生id为查找的get(index)
*/
Map<Integer, String> teacherMap = teachers.stream().collect(Collectors.toMap(s -> s.getId(), s -> s.getToken()));
int count=0;
for (Student student : students) {
if(teacherMap.get(student.getId())!=null && !teacherMap.get(student.getId()).equals("")) {
count++;//30000
System.out.println("执行的token:"+teacherMap.get(student.getId()));
student.setToken(teacherMap.get(student.getId()));
//执行时长:347 毫秒
}
}
可以从执行结果看到,不到0.5秒就完成了,这就是非常能接受了,那想想为什么?
那我们从时间复杂度分析一下:第一种将近
的时间复杂度,把每一个元素都比一下,慢是自然的。
那Map的底层结构我们众所周知,从8开始他就是数组+链表的形式,map的取值效率在多数的情况下是能维持接近O(1) 的,所以用map那是嘎嘎快。
今天分析到此结束,学会一个小知识,希望以后能用得上