Under what conditions is StringBuider more efficient?

introduction

It is said that String builder is more efficient than String in dealing with String splicing, but sometimes there may be some deviation in our understanding.

Recently, when I tested the efficiency of data import, I found that my previous understanding of string builder was wrong. Later, I found out the logic of this piece by means of practical test + principle finding. Now let's share the process

test case

There are generally two cases when our code splices strings in a loop

  • The first is to splice several fields in the object into a new field each time, and then assign a value to the object

  • The second operation is to create a string object outside the loop and splice new content to the string each time. After the loop ends, the spliced string is obtained

For both cases, I created two control groups

Group 1:

Concatenate strings in each For loop, that is, spell and use, and destroy when used up. Use String and StringBuilder to splice respectively

/**  
 * String splices strings in the loop, and destroys the java framework project www.1b23.com after one loop com   
 */  
public static void useString(){  
    for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {  
        String str = str1 + i + str2 + i + str3 + i + str4 ;  
    }  
}  
  
/**  
 * Use StringBuilder to splice strings in the loop and destroy them after one loop  
 */  
public static void useStringBuilder(){  
    for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {  
        StringBuilder sb = new StringBuilder();  
        String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString();  
    }  
}   

Group 2:

Multiple For loops splice a string. The string is used at the end of the loop, and then recycled by the garbage collector. String and StringBuilder are also used For splicing respectively

/**  
 * Concatenate multiple loops into a string String
public static void useStringSpliceOneStr (){  
    String str = "";  
    for (int i = 0; i < CYCLE_NUM_LOWER; i++) {  
        str += str1 + str2 + str3 + str4 + i;  
    }  
}  
  
/**  
 * Concatenate multiple loops into a string with StringBuilderjava Framework project www.1b23.com com 
 */  
public static void useStringBuilderSpliceOneStr(){  
    StringBuilder sb = new StringBuilder();  
    for (int i = 0; i < CYCLE_NUM_LOWER; i++) {  
        sb.append(str1).append(str2).append(str3).append(str4).append(i);  
    }  
}   

In order to ensure the test quality, before each test item is carried out. The thread rested for 2s, and then ran for 5 times to warm up. Finally, calculate the time by averaging the time for 5 times

public static int executeSometime(int kind, int num) throws InterruptedException {  
    Thread.sleep(2000);  
    int sum = 0;  
    for (int i = 0; i < num + 5; i++) {  
        long begin = System.currentTimeMillis();  
  
        switch (kind){  
            case 1:  
                useString();  
                break;  
            case 2:  
                useStringBuilder();  
                break;  
            case 3:  
                useStringSpliceOneStr();  
                break;  
            case 4:  
                useStringBuilderSpliceOneStr();  
                break;  
            default:  
                return 0;  
        }  
  
        long end = System.currentTimeMillis();  
  
        if(i > 5){  
            sum += (end - begin);  
        }  
    }  
    return sum / num;  
}   

Main method

//java Framework project www.1b23.com 
public class StringTest {  
    public static final int CYCLE_NUM_BIGGER = 10_000_000;  
    public static final int CYCLE_NUM_LOWER = 10_000;  
    public static final String str1 = "Zhang San";  
    public static final String str2 = "Li Si";  
    public static final String str3 = "Wang Wu";  
    public static final String str4 = "Zhao Liu";  
  
  
    public static void main(String[] args) throws InterruptedException {  
        int time = 0;  
        int num = 5;  
  
        time = executeSometime(1, num);  
        System.out.println("String Splicing "+ CYCLE_NUM_BIGGER +" Times," + num + "Average time:" + time + " ms");  
  
        time = executeSometime(2, num);  
        System.out.println("StringBuilder Splicing "+ CYCLE_NUM_BIGGER +" Times," + num + "Average time:" + time + " ms");  
  
        time = executeSometime(3, num);  
        System.out.println("String Splice a single string "+ CYCLE_NUM_LOWER +" Times," + num + "Average time:" + time + " ms");  
  
        time = executeSometime(4, num);  
        System.out.println("StringBuilder Splice a single string "+ CYCLE_NUM_LOWER +" Times," + num + "Average time:" + time + " ms");  
  
    }  
}    

test result

The test results are as follows

Result analysis

first group

10_ 000_ 000 times of loop splicing, the efficiency of using String and StringBuilder in the loop is the same! Why? Take a look at this article: can't you use + to splice strings? You know. Follow the Java technology stack official account to reply to Java for more Java dry goods tutorials.

Use javap - C stringtest Class decompile view the files compiled by the two methods:

It can be found that the String method splices the String, and the compiler uses String builder after optimization. Therefore, the efficiency of use case 1 and use case 2 is the same.

Group 2

The result of the second group is what everyone likes to see and hear, because 10_ 000_ 000 loop String splicing is too slow, so I used 10_ 000 splices to analyze.

Analysis case 3: Although the compiler will optimize String splicing, it creates a String builder object in the loop every time and destroys it in the loop.

The next cycle he has created. Compared with case 4, the new object is created outside the loop for n times, the operation of destroying the object, and the operation of converting String builder into String for n - 1 times. Low efficiency is also a matter of course.

extend

There is another way to write the test of the first group:

/**  
 * Use StringBuilder to splice strings in the loop, and destroy the java framework project www.1b23.com after one loop com  
 */  
public static void useStringBuilderOut(){  
    StringBuilder sb = new StringBuilder();  
    for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {  
//            sb.setLength(0);  
        sb.delete(0, sb.length());  
        String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString();  
    }  
}   

Each time the String builder creates the outer loop, it clears the contents of the String builder. This way of writing, no matter using sb setLength(0); Or sb delete(0, sb.length()); It is more efficient than using String / directly in the loop StringBuilder Slow down.

However, I have no idea why he is slow. I guess the speed of the new object is slower than the length, so I tested the following:

public static void createStringBuider() {  
    for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {  
        StringBuilder sb = new StringBuilder();  
    }  
}  
  
public static void cleanStringBuider() {  
    StringBuilder sb = new StringBuilder();  
    for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {  
        sb.delete(0, sb.length());  
    }  
}   

But the result is that cleanStringBuider is faster.

conclusion

  • The compiler will optimize String splicing to use String builder, but there are still some defects. It is mainly reflected in the use of String splicing in the loop, and the compiler will not create a single String builder to reuse

  • For the requirement of splicing a String in multiple loops: StringBuilder is fast, because it avoids n operations of new object and destroying object, and n - 1 operations of converting StringBuilder into String

  • StringBuilder splicing is not applicable to the operation mode of each splicing in the loop. Because the compiler optimized String splicing also uses String builder, which has the same efficiency. The latter is convenient to write

Tags: Java

Posted by gausie on Wed, 25 May 2022 06:05:45 +0300