[source code analysis] there are differences among String, StringBuffer and StringBuilder.

Posted by Charlie9809 on Tue, 07 Dec 2021 17:58:50 +0100

🎈🎈🎈🎈🎈 Previous recommendations:
🎍 On the five components of spring MVC and the analysis of execution principle.
🎍 Quickly learn about Java virtual machine (JVM) and common interview questions (constantly updating...)
🎍 I am grateful for the experience of this half year and hope to live up to expectations in the future. (Qiu Zhao's experience)
🎍 Initial Big Data development.

During the interview some time ago, I found that the difference between string, StringBuffer and StringBuilder is almost a necessary question in the interview, and it is used very frequently in future development. Understanding the underlying principles will greatly improve the development efficiency in the future, so I summarize it here.

Relationship among the three

When many students first come into contact with these three classes, they always don't know the relationship between them. Let's analyze from the JDK source code:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
		}
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuffer>, CharSequence{
    }
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence{
    }
abstract class AbstractStringBuilder implements Appendable, CharSequence {}

StringBuffer and StringBuilder inherit from AbstractStringBuilder, while AbstractStringBuilder and String inherit from Object (Object is the superclass of all java classes). Therefore, the relationship between these three classes can be roughly expressed as follows:

String

As can be seen from the above figure, the bottom layer of String is actually composed of char array and decorated with final keyword, which shows that String objects cannot be changed and inherited. The original String will never change. Instead, copy a copy, add the text to be connected to the copied String, and finally return a new String. When operating on a large number of strings, the String class can cause serious memory leaks and time delays.

So, what is the implementation of using "+" to splice strings?
As in the above code, first create a String object a, and then assign "abc" to it. Later, the Java virtual machine creates a String object a, and then add the value of the original A and hello to assign it to the new a. the original a will be recycled by the garbage collection mechanism (GC) of the Java virtual machine. Therefore, a has not been changed, That is, the String object mentioned earlier cannot be changed once it is created. It can be seen from here that String type is not recommended for frequently operated strings. It will be a process of constantly creating new objects and recycling old objects, so the execution speed is very slow.

StringBuffer

From the source code, we can see that for StringBuffer, the bottom layer is also a char array. The default initial space of StringBuffer is 16. For the capacity expansion of StringBuffer, it can be seen from the following that it is twice the old array, and then add 2 to expand the capacity.

  public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0) {
            ensureCapacityInternal(minimumCapacity);
        }
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        if (minimumCapacity - oldCapacity > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity) << coder);
        }
    }

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        int newCapacity = (oldCapacity << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
        return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

    private int hugeCapacity(int minCapacity) {
        int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
        int UNSAFE_BOUND = Integer.MAX_VALUE >> coder;
        if (UNSAFE_BOUND - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > SAFE_BOUND)
            ? minCapacity : SAFE_BOUND;
    }

Next, let's take a look at the operation function append of StringBuffer. The append method is modified by synchronized and is thread safe. Suitable for multi-threaded environment

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

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

StringBuilder

public StringBuilder() {
    super(16);
}
public StringBuilder(int capacity) {
    super(capacity);
}
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

By comparing with the source code of StringBuffer, we find that the initial space is also 16. Of course, you can also specify the initial capacity or assign an initial value to the StringBuilder object with an existing character sequence. StringBuilder and StringBuffer are inherited from AbstractStringBuilder, so their initial space and expansion are the same.

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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

The difference between StringBuilder and StringBuffer can be seen from the above. For the append() method, the synchronized modifier is missing, which makes StringBuilder not thread safe. StringBuilder is suitable for single threaded environments

performance comparison

public class Text1 {
    public static void main(String[] args) {
   
        int num = 100000;
        String a = "abc";
        long time = System.currentTimeMillis();
        for (int i = 1; i < num; i++) {
            a = a + i;
        }
        System.out.println(System.currentTimeMillis() - time);

        long time1 = System.currentTimeMillis();
        StringBuffer bf = new StringBuffer();
        for (int i = 1; i < num; i++) {
            bf.append(i);
        }
        System.out.println(System.currentTimeMillis() - time1);

        StringBuilder builder = new StringBuilder();
        long time2 = System.currentTimeMillis();
        for (int i = 1; i < num; i++) {
            builder.append(i);
        }
        System.out.println(System.currentTimeMillis() - time2);
    }
}

The operation results are as follows:

4134
4
3

Process finished with exit code 0

The efficiency of the three is: StringBuilder > StringBuffer > string.

summary

  • The string class is decorated by the final keyword. String is an immutable type
  • The original string will never change. Instead, copy a copy, add the text to be connected to the copied string, and finally return a new string
  • When operating on a large number of strings, the String class can cause serious memory leaks and time delays.
  • StringBuilder and StringBuffer are variable strings. The former is thread unsafe and the latter is thread safe.
  • Most methods of StringBuilder and StringBuffer call the implementation of the parent class AbstractStringBuilder. The capacity expansion mechanism first changes the capacity to twice the original capacity plus 2. The maximum capacity is Integer.MAX_VALUE, that is 0x7fffff.
  • The default capacity of StringBuilder and StringBuffer is 16. It is best to estimate the size of the string in advance to avoid the time consumption caused by capacity expansion.
    -In terms of performance, StringBuilder > StringBuffer > string.

December 7, 2021
I can't think of Xiao Zhao.
——Migrant workers in the new era 🙊
——Look at the world with another kind of thinking logic 👀
Today is the 1011st day of joining CSDN. I think it's helpful. Please praise, comment and collect.

Topics: Java Interview string JavaSE