refer to:
https://blog.csdn.net/rusbme/article/details/51389623
https://blog.csdn.net/matrix5267/article/details/62423340
String String constant
StringBuffer String variable (thread safe)
StringBuilder string variable (not thread safe)
String a= new String("hello");
1: An a object reference is defined in the stack memory, pointing to the memory address of the value "hello" in the heap memory. Finally opened up a memory space
2: Redefine an a object reference in the stack memory, first point to the memory address of "hello" in the heap memory, and then point to the address of "hello" in the heap memory after new. Finally, two spaces are opened up. The first space has no object references and will be garbage collected by the JVM.
str += "andy";
Isn't the value of str also changed?
In fact, the above code has opened up 3 spaces in memory, namely: "hello", "andy", "helloandy", their heap memory size is fixed, and finally str points to the heap address of "helloandy". When StringBuffer is used, it will only open up a memory space, and you can use append to add operations such as delete.
First let's look at the inheritance system of the two classes
public final class StringBuilder extends AbstractStringBuilder implements Appendable, CharSequence, Serializable; public final class StringBuffer extends AbstractStringBuilder implements Appendable, Serializable, CharSequence
AbstravtStringBuilder类
private char[] value; static final int INITIAL_CAPACITY = 16; AbstractStringBuilder() { value = new char[INITIAL_CAPACITY]; } AbstractStringBuilder(int capacity) { if (capacity < 0) { throw new NegativeArraySizeException(Integer.toString(capacity)); } value = new char[capacity]; } AbstractStringBuilder(String string) { count = string.length(); shared = false; value = new char[count + INITIAL_CAPACITY]; string.getCharsNoCheck(0, count, value, 0); }
Constructor:
//The construction methods of the two classes are exactly the same, both call the constructor of the parent class, and leave 16 spaces publicStringBuilder() { 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); }
The default initial capacity is 16, of course, we can also set the capacity ourselves; then we look at the splicing process
final void append0(char[] chars) { int newCount = count + chars.length; if (newCount > value.length) { enlargeBuffer(newCount); } System.arraycopy(chars, 0, value, count, chars.length); count = newCount; }
Next, let's take a look at the SB expansion process .
void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
Try to expand the new capacity to 2 times the size + 2. If the capacity is not enough, directly expand to the required capacity. Therefore, when we create SB, we can estimate the length of the new string to properly avoid multiple expansions, which will affect efficiency.
Basic method:
append
//Look at StringBuilder first public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } //StringBuffer, CharSequence, char[] are similar public StringBuilder append(String str) { super.append(str); return this; } //boolean, char, long, float, double are similar public StringBuilder append(int i) { super.append(i); return this; }
//StringBuffer public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; } //StringBuffer, CharSequence, char[] are similar public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } //boolean, char, long, float, double are similar public synchronized StringBuffer append(int i) { toStringCache = null; super.append(i); return this; }
insert
//StringBuilder public StringBuilder insert(int index, char[] str, int offset, int len) { super.insert(index, str, offset, len); return this; } public StringBuilder insert(int offset, Object obj) { super.insert(offset, obj); return this; } public StringBuilder insert(int offset, int i) { super.insert(offset, i); return this; }
//StringBuffer publicsynchronized StringBuffer insert(int index, char[] str, int offset, int len) { toStringCache = null; super.insert(index, str, offset, len); return this; } public synchronized StringBuffer insert(int offset, Object obj) { toStringCache = null; super.insert(offset, String.valueOf(obj)); return this; } public StringBuffer insert(int offset, int i) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of i to String by super class method // Ditto for toStringCache clearing super.insert(offset, i); return this; }
- 3
- 4
- 5
delete
//StringBuilder publicStringBuilder delete(int start, int end) { super.delete(start, end); return this; } public StringBuilder deleteCharAt(int index) { super.deleteCharAt(index); return this; } public StringBuilder replace(int start, int end, String str) { super.replace(start, end, str); return this; }
//StringBuffer public synchronized StringBuffer delete(int start, int end) { toStringCache = null; super.delete(start, end); return this; } public synchronized StringBuffer deleteCharAt(int index) { toStringCache = null; super.deleteCharAt(index); return this; } public synchronized StringBuffer replace(int start, int end, String str) { toStringCache = null; super.replace(start, end, str); return this; }
reverser
public StringBuilder reverse() { super.reverse(); return this; } publicsynchronized StringBuffer reverse() { toStringCache = null; super.reverse(); return this; }
//StringBuilder public String toString() { // Create a copy, don't share the array return new String(value, 0, count); } //StringBuffer public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
Unique to StringBuffer:
//If a method modifies the value, there will be a problem using the length() method synchronously public synchronized int length() { return count; } public synchronized int capacity() { return value.length; } public synchronized void ensureCapacity(int minimumCapacity) { if (minimumCapacity > value.length) { expandCapacity(minimumCapacity); } } //The same reason, this also adds synchronized public synchronized char charAt(int index) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); return value[index]; } public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { super.getChars(srcBegin, srcEnd, dst, dstBegin); }
public synchronized void setLength(int newLength) { toStringCache = null; super.setLength(newLength); } public synchronized void setCharAt(int index, char ch) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); toStringCache = null; value[index] = ch; }
public synchronized String substring(int start) { return substring(start, count); } public synchronized String substring(int start, int end) { return super.substring(start, end); } publicsynchronized CharSequence subSequence(int start, int end) { return super.substring(start, end); }