Interview Question Series No. 8: Talk about the difference between String, StringBuffer and StringBuilder?

"Java Interview Question Series": A long knowledge and very interesting column. In-depth digging, analyzing the source code, summarizing the principles, combining pictures and texts, creating a series of articles on the official account, and raising the level for interviews or not Welcome to continue to pay attention to [Program New Vision]. Article 8.

In addition to memory distribution and equals comparison, the most common interview questions about strings are the differences between StringBuffer and StringBuilder.

If you answer: String class is immutable, StringBuffer and StringBuilder are mutable classes, StringBuffer is thread-safe, StringBuilder is not thread-safe.

In terms of the above summary, it seems that I know a little bit. This article will lead you to a comprehensive understanding of the three differences and the underlying implementation.

String concatenation

Many articles about String have been described in detail in the previous articles, and its immutability is also caused by the generation of a new string in memory whenever the "+" operation is passed.

String a = "hello ";
String b = "world!";
String ab = a + b;

For the above code, the memory distribution diagram is as follows:

image

Among them, a and b are in the string constant pool when they are initialized, and the spliced ​​object ab is in the heap. It can be seen intuitively that a String object is newly generated after splicing. If spliced ​​multiple times, multiple intermediate objects will be generated.

The above conclusion was established before Java 8. In Java 8, the JDK optimized the splicing of the "+" sign. The splicing method written above will be optimized for processing based on the append method of StringBuilder.

stack=2, locals=4, args_size=1
     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String world!
     5: astore_2
     6: new           #4                  // class java/lang/StringBuilder
     9: dup
    10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
    13: aload_1
    14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    17: aload_2
    18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    24: astore_3
    25: return

The above is the result of decompiling bytecode through javap -verbose command. Obviously, you can see the creation of StringBuilder and the call of append method.

At this point, if you answer in a general way: concatenating strings through the plus sign will create multiple String objects, so the performance is worse than StringBuilder, which is wrong. Because the effect of splicing plus sign is essentially the same as StringBuilder after being processed by the compiler.

If you use the following wording in your code:

StringBuilder sb = new StringBuilder("hello ");
sb.append("world!");
System.out.println(sb.toString());

The compiler plug-in even suggests that you use String instead.

Comparison of StringBuffer and StringBuilder

The core codes of StringBuffer and StringBuilder are basically the same, and many codes are common. Both of these classes inherit from the abstract class AbstractStringBuilder.

Let's take a look at the differences from the construction method to the append method one by one. First look at the construction method of StringBuilder:

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

The super method is the construction method of the AbstractStringBuilder called. The same is true in the construction method of the corresponding StringBuffer:

public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

From the construction method, StringBuffer and StringBuilder are the same. Let's take a look at the append method. StringBuilder is implemented as follows:

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

The method corresponding to StringBuffer is as follows:

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

Obviously, in addition to the internal assignment of the toStringCache variable to null in the implementation of the append method of StringBuffer, the only difference is that the method uses synchronized for synchronization processing.

toStringCache is used to cache the string generated the last time the toString method is called. When the content of the StringBuffer changes, the changed value will also change.

Through the comparison of the append method above, we can easily find that StringBuffer is thread-safe and StringBuilder is non-thread-safe. Of course, using synchronized for synchronization processing will reduce performance a lot.

Low-level implementation of StringBuffer and StringBuilder

Both StringBuffer and StringBuilder call the construction method of the parent class:

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

Through this construction method, we can see that the key attribute they use to process string information is value. Initialize a char[] array whose length is the length of the incoming string +16, which is the value value, to store the actual string during initialization.

After calling the parent class's construction method, the respective append method is called (see the previous code), and the core processing in it calls the parent class's append method:

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

In the above code, the str.getChars method is used to splice the incoming str string and fill it after the original value array. And count is used to record the length that has been used in the current value number.

image

So, when the synchronized operation is not used for synchronization, where does thread insecurity occur? The count+=len in the above code is not an atomic operation. For example, the current count is 5, and the two threads perform the ++ operation at the same time, and the values ​​obtained are both 5. After the addition operation is performed, the value is assigned to count, and the two threads are assigned the value 6 instead of 7. At this time, there is a problem of thread insecurity.

Why String should be designed to be immutable

The design of String to be immutable in Java is the result of comprehensive consideration of various factors, for the following reasons:

1. The need of string constant pool. If the string is variable, changing one object will affect another independent object. This is also a prerequisite for the existence of a string constant pool.

2. The hash code of String objects in Java is frequently used, such as in containers such as HashMap. The constant string guarantees the uniqueness of the hash code, which can be cached and used.

3. Security, to ensure that String remains unchanged when passed as a parameter to avoid security risks. For example, the database user name, password, access path, etc. remain unchanged during the transmission process to prevent the value of the object pointed to by the changed string from being changed.

4. Since string variables are immutable, they can be shared and used in multiple threads.

summary

We all know simple rote test questions, but we must learn more about the underlying implementation principles in the process of memorizing the interview questions, which will not only help understand the "why", but also learn more relevant knowledge and principles.

In this article, the explanation of the steps of copying the internal data of StringBuilder and StringBuffer and expanding the array is simplified. Those who are interested can continue to conduct in-depth research against the source code.

Original link: " Interview Question Series No. 8: Talk about the difference between String, StringBuffer, StringBuilder?


New Vision of Procedure

The public account " New Vision of Program ", a platform that allows you to simultaneously improve your soft power and hard technology, providing massive amounts of data

WeChat Official Account: New Vision of Program

Guess you like

Origin blog.csdn.net/wo541075754/article/details/108604389