[Java] Using HashMap in Java to optimize multi-layer for loop nesting and performance optimization of for loop

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.

Guess you like

Origin blog.csdn.net/u011397981/article/details/131783676