Article Directory
foreword
The for loop is one of the commonly used grammars in development, such as traversing arrays and collections, etc., but if it is not used properly, there will be a lot of new energy loss problems. Today, I will explain the common performance optimization issues of the for loop.
There is also a for loop in the for loop, and then do some data matching to handle this scenario.
Optimizing multi-level nested loops using HashMap
The time complexity of the for loop nested n times in m layers is O(n^m), as follows:
before optimization
public class forTradition {
public static void main(String[] args){
List<Student> stus = new ArrayList<>();
// 为stus写入10条数据 ... 略
List<Grade> gs = new ArrayList<>();
// 为gs写入10条数据 ... 略
for (int i = 0 ; i < stus.size() ; i++ ) {
Student stu = stus.get(i);
Integer id = stu.getId();
String stuName = stu.getName();
for (int j = 0 ; j < gs.size() ; j++ ) {
Grade g = gs.get(j);
if( id == g.getStuId() ) {
System.out.println( "学生:" + stuName + ",成绩:" + g.getValue() );
}
}
}
}
Optimized
public class forNew {
public static void main(String[] args){
List<Student> stus = new ArrayList<>();
// 为stus写入10条数据 ... 略
List<Grade> gs = new ArrayList<>();
// 为gs写入10条数据 ... 略
Map<Integer,Integer> gradesMap =
gs.stream().collect(Collectors.toMap( data -> data.getStuId() , data -> data.getValue() );
for (int i = 0 ; i < stus.size() ; i++ ) {
Student stu = stus.get(i);
Integer value = gradesMap.get(stu.getId());
if( null != value ) {
System.out.println( "学生:" + stu.getName() + ",成绩:" + value );
}
}
}
}
Performance optimization of for loop
nested loop
Nested loops are two or more layers of loops nested together, and the code description is directly below.
Outer big inner small nesting
/**
* 大循环驱动小循环(即外大内小)
*/
private static void bigSmall() {
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
for (int j = 0; j < 100; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("外大内小耗时:" + (endTime - stratTime));
}
Execute to see the time-consuming results:
外大内小耗时:8743800
Look at the time-consuming situation of small outside and big inside
Small outside and big inside
/**
* 小循环驱动大循环(即外小内大)
*/
private static void smallBig() {
long stratTime = System.nanoTime();
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 10000000; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("外小内大耗时:" + (endTime - stratTime));
}
Execute to see the time-consuming results:
外小内大耗时:6922600
Well, let's compare the execution time of the two comprehensively, and the time difference is still very large.
Time-consuming external small internal large: 6922600; external large internal small time-consuming: 8743800
analysis Summary
From the above comparison, it can be seen that the performance is significantly improved after optimization. Nested loops should follow the principle of "small outside and big inside". Although the number of loops has not changed, it takes a lot of time. This is like the difference between copying many small files and copying several large files. Although the total size has not changed, copying large files is obviously faster than multiple small files.
instantiation of the loop variable
Put an instance of the loop variable inside the loop:
/**
* 循环变量放在循环内
*/
private static void smallBigBetterTwo() {
long stratTime = System.nanoTime();
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 10000000; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("循环内变量耗时:" + (endTime - stratTime));
}
Execution time-consuming:
循环内变量耗时:4934500
Put the instance of the loop variable outside the loop:
/**
-
The loop variable is placed outside the loop
*/
private static void smallBigBetter() { long stratTime = System.nanoTime(); int i, j; for (i = 0; i < 100; i++) { for (j = 0; j < 10000000; j++) {
}
}
long endTime = System.nanoTime();
System.out.println("The variable outside the loop takes time: " + (endTime - stratTime));
}
Execution time-consuming:
循环外变量耗时:5013800
Comparing the time-consuming comparison between putting variables inside the loop and outside the loop, I found that the time difference is still quite large:
Time-consuming variables in the loop: 4934500; time-consuming variables outside the loop: 5013800
analysis Summary
Although the optimization effect is not obvious, as the number of cycles increases, the time-consuming will increase, and the optimization effect will become more and more obvious. Analysis: 1+i=101 instantiations are required before optimization, but only 2 times after optimization. Summary: The instantiation of loop variables should be placed outside the loop.
Extract expressions not related to loops
Extraneous expressions are not extracted
/**
* 未提取无关的表达式
*/
private static void calculationInner() {
int a = 3;
int b = 7;
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
i = i * a * b;
}
long endTime = System.nanoTime();
System.out.println("未提取耗时:" + (endTime - stratTime));
}
No extraction time consuming:
未提取耗时:800
Extraneous expressions extracted
/**
* 提取无关的表达式
*/
private static void calculationOuter() {
int a = 3;
int b = 7;
int c = a * b;
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
i = i * c;
}
long endTime = System.nanoTime();
System.out.println("已提取耗时:" + (endTime - stratTime));
}
Time-consuming extraction of irrelevant expressions:
已提取耗时:500
analysis Summary
In the code, a*b has nothing to do with the loop, so it should be placed outside to avoid repeated calculations. From a theoretical point of view, due to the reduction in the number of calculations, the performance after optimization will be higher.
Eliminate the method call when the loop terminates the judgment
stratTime = System.nanoTime();
for (int i = 0; i < list.size(); i++) {
}
endTime = System.nanoTime();
System.out.println("未优化list耗时:"+(endTime - stratTime));
time consuming:
未优化list耗时:253800
Optimized
stratTime = System.nanoTime();
int size = list.size();
for (int i = 0; i < size; i++) {
}
endTime = System.nanoTime();
System.out.println("优化list耗时:"+(endTime - stratTime));
time consuming:
优化list耗时:142500
analysis Summary
Every time in the loop, list.size() will be executed once, which will undoubtedly affect the performance of the program, so it should be placed outside the loop, and a variable is used to cache its size. Don’t let this little bit of code consume us so much performance.
exception capture
catch exception inside
/**
* 在内部捕获异常
*/
private static void catchInner() {
long stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
try {
} catch (Exception e) {
}
}
long endTime = System.nanoTime();
System.out.println("在内部捕获异常耗时:" + (endTime - stratTime));
}
Execution time-consuming:
在内部捕获异常耗时:3352700
Catch exceptions externally
/**
* 在外部捕获异常
*/
private static void catchOuter() {
long stratTime = System.nanoTime();
try {
for (int i = 0; i < 10000000; i++) {
}
} catch (Exception e) {
}
long endTime = System.nanoTime();
System.out.println("在外部捕获异常耗时:" + (endTime - stratTime));
}
Execution time-consuming:
在外部捕获异常耗时:2893600
analysis Summary:
Catching exceptions takes up a lot of resources, so don't put try catch inside the loop. After optimization, the performance will also be improved by several orders of magnitude. In addition, the book "Effective Java" points out that the for-each loop is preferred over the traditional for loop. It has advantages that the traditional for loop cannot match in terms of simplicity and bug prevention, and there is no performance loss. Therefore, it is recommended to use for -each loop.