This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

This article was first published on CSDN, so there are also CSDN watermarks on the pictures. All the articles are original and there is no infringement

String and StringBuilder and StringBuffer

String parsing

preface

String is a reference type, not a primitive type. The underlying type is composed of an array of char objects. We say that strings are immutable and cannot be modified once created.

In fact, we can find in the source code is actually a new String object, assign it, return a new String object, is not the original object.

String concat();

public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); // We can see that this is a new object}Copy the code

For StringBuffer and StringBuilder, the address does not change after executing their concatenation methods, because their underlying char arrays can be expanded and remain the same address. (We’ll look at StringBuffers and StringBuilders in more detail later.)

The inheritance system of String objects
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence 
Copy the code
  • String is final, which means that it cannot be inherited
  • The Serializable interface is implemented, which means it can be serialized and deserialized
  • The Comparable interface is implemented, which means you can compare string objects
  • The CharSequence interface is implemented to indicate that this is a string
Member variables
private final char value[]; Private int hash; private int hash; // Default to 0, hash, Default to 0Copy the code
A constructor
Public String() {this.value = "".value; // Note here: } // Pass in a String constructor, Public String(String original) {this.value = original. Value; this.hash = original.hash; Public String(char Value []) {this.value = array.copyof (value, value.length); } public String(byte bytes[]) {this(bytes, 0, bytes.length); } public String(synchronized(buffer) {this.value = synchronized(buffer) Arrays.copyOf(buffer.getValue(), buffer.length()); Public String(StringBuilder Builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length()); }Copy the code
Members of the method
int length()

Mandatory String Length of the object

public int length() {
    return value.length;
}
Copy the code
boolean isEmpty()

Checks if the string is empty

public boolean isEmpty() {
    return value.length == 0;
}
Copy the code
char charAt(int index)

Gets the char character at the index

Public char charAt (int index) {if ((index < 0) | | (index > = value. The length)) {/ / whether the index illegal throw new StringIndexOutOfBoundsException(index); } return value[index]; }Copy the code
byte[] getBytes()

Converts a String to a byte array and returns it

public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}
Copy the code
boolean equals(Object anObject)

Determine if the parameter passed is equal to itself (content equality)

String overrides equals of an Object, rewriting equals as a method to compare whether content is equal or not.

Public Boolean equals(Object anObject) {if (this == anObject) {if (this == anObject) {return true; } if (anObject instanceof String) {// If (anObject instanceof String) {String anotherString = (String)anObject; Int n = value.length; / / here it is of type String, get the length of the String if (n = = anotherString. Value. Length) {/ / equal length can continue to compare, not equal to direct the interrupt return false char v1 [] = value; char v2[] = anotherString.value; int i = 0; while (n-- ! If (v1[I]! = 0) {// if (v1[I]! = v2[i]) return false; // return false i++; } return true; }} return false; }Copy the code
boolean equalsIgnoreCase(String anotherString)

Ignore case to determine equality (content equality)

public boolean equalsIgnoreCase(String anotherString) { return (this == anotherString) ? true : (anotherString ! = null) && (anotherString.value.length == value.length) && regionMatches(true, 0, anotherString, 0, value.length); }Copy the code
int compareTo(String anotherString)

Compare the size of two strings (lexicographical comparison)

The underlying char arrays are compared one by one until the characters in one char array are not equal to the characters in the corresponding positions in the other char array.

Compare string lengths if the characters are equal up to the unprecedented surface of a char array

public int compareTo(String anotherString) { int len1 = value.length; / / get the length of the string itself int len2 = anotherString) value. Length; Int lim = math.min (len1, len2); Char v1[] = value; char v2[] = anotherString.value; int k = 0; While (k < lim) {char c1 = v1[k]; while (k < lim) {char c1 = v1[k]; char c2 = v2[k]; if (c1 ! = c2) { return c1 - c2; } k++; } return len1 - len2; }}Copy the code
compareToIgnoreCase(String str)

Compare the size of two strings regardless of case

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}
Copy the code
String substring(int beginIndex, int endIndex)

Intercepts a String (from beginIndex to endindex-1, excluding characters at endIndex)

As you can see from the source code below, the so-called interception of a String also creates a new String object, which again confirms the statement that String strings are immutable

public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); // The String returned by interception is new}Copy the code
int hashcode()

Since String doesn’t inherit classes, its parent is Object. The hashcode() method in Object returns an address, while String overrides hashcode() to return a hash of content. That is, if two strings have the same content, The values returned using hashcode() must also be equal

If hashcode() is not overridden, strings with the same contents will not be mapped to the same address. If hashcode() is overridden, strings with the same contents will not be mapped to the same address. There’s less hash conflict when you pick the base of a prime number, but why do you pick 31? I think it’s to make hashing less likely. Anyway, all this is about reducing the probability of hash collisions

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}
Copy the code
Constant pool

In Java, == compares addresses

When you create a string using double quotes, you create the string first and then place it in the constant pool. If you create the same string using double quotes, you retrieve it from the constant pool instead of creating it again. The following results also illustrate the existence of constant pools

If you create a string using new (even if you create a string from the constant pool, it is not taken from the constant pool), you create a new one in the heap

== == == == == == == =

Finally, let’s talk about how the + sign concatenates strings in strings

The concatenation of two constants is a string created using double quotes

Public class Main {public static void Main (String[] args) {String s1 = "ABC "; String s2 = new String("abc"); String s3 = "abc" + "edf"; // Add system.out.println (s1 == s2) to system.out.println (s1 == s2); Public class Main {public Main() {} public static void Main(String[] args) {String s1 = "ABC "; String s2 = new String("abc"); String s3 = "abcedf"; System.out.println(s1 == s2); system.out. println(s1 == s2); }}Copy the code

The concatenation of two variables is essentially a New StringBuilder object

Public class Main {public static void Main (String[] args) {String s1 = "ABC "; String s2 = new String("abc"); String s3 = s1 + s2; Public class Main {public Main() {} public static void Main(String[] args) {String s1 = "ABC ";  String s2 = new String("abc"); (new StringBuilder ()). Append (s1), append (s2). The toString (); // Essentially new a StringBuilder to concatenate}}Copy the code

Because StringBuffer is very similar to the source code for StringBuilder, we’ll put the two classes together

StringBuffer and StringBuilder parsing

preface

StringBuffer and StringBuilder are mutable strings with a char array at the bottom. How do we implement such a mutable string class? Let’s take a look!

Inheritance system
Public Final Class StringBuffer extends AbstractStringBuilder implements Java.io.Serializable Public Final Class StringBuilder extends AbstractStringBuilder implements public Final Class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequenceCopy the code
  • StringBuffer and StringBuilder are final, meaning that they cannot be inherited
  • AbstractStringBuilder class inherits AbstractStringBuilder parent class with some member variables and methods like Append (), INSERT (),delete(), etc
  • The Serializable interface is implemented, indicating that serialization and deserialization are possible
  • The CharSequence is implemented, indicating that this is a string
Member variables

StringBuffer and StringBuilder have only one recorded version of a member variable, so it doesn’t matter to us that these two classes have a parent class.

Here are the member variables of its parent class, AbstractStringBuilder

Char [] value; char[] value; // The underlying char array, which is dynamic, will be expanded when the capacity is insufficient int count; // The number of characters actually storedCopy the code
A constructor
Constructor of StringBuffer
Public StringBuffer() {super(16); } public StringBuffer(int capacity) {super(capacity); } public StringBuffer(String STR) {super(str.length() +16); append(str); }}}}}}}}}}}}}}Copy the code
Constructor of StringBuilder
Public StringBuilder() {super(16); public StringBuilder() {super(16); } public StringBuilder(int capacity) {super(capacity); } public StringBuilder(String STR) {super(str.length() +16); append(str); // This StringBuilder overrides the append method, essentially calling the parent append method}Copy the code

Abstractstringbuffer constructor class AbstractStringBuilder constructor class AbstractStringBuilder constructor class AbstractStringBuilder constructor class AbstractStringBuilder constructor class AbstractStringBuilder constructor

AbstractStringBuilder constructor
AbstractStringBuilder() {value = new char[capacity]; AbstractStringBuilder() {value = new char[capacity]; }Copy the code
Members of the method
append()

Append of StringBuffer (method)

Concatenation (can concatenate String, StringBuffer, StringBuilder, Boolean, int, float), return type is StringBuffer

We can see that these concatenation methods are actually concatenation methods that call their AbstractStringBuilder parent, but in StringBuffer they all use the synchronized modifier, indicating thread-safe.

Public synchronized StringBuffer appEnd (Object obj) {toStringCache = null; super.append(String.valueOf(obj)); return this; String @override public synchronized StringBuffer appEnd (String STR) {toStringCache = null; super.append(str); return this; } // concatenate the StringBuffer, which is defined by the subclass itself, Public synchronized StringBuffer appEnd (StringBuffer sb) {toStringCache = null; super.append(sb); return this; AbstractStringBuilder @override synchronized StringBuffer append(AbstractStringBuilder asb) { toStringCache = null; super.append(asb); return this; } @override public synchronized StringBuffer appEnd (char[] STR) {toStringCache = null; super.append(str); return this; } @override public synchronized StringBuffer appEnd (Boolean b) {toStringCache = null; super.append(b); return this; } @override public synchronized StringBuffer appEnd (char c) {toStringCache = null; super.append(c); return this; }Copy the code

Append () method of StringBuilder

Unlike StringBuffer, these methods do not use the synchronized modifier, indicating that they are not thread-synchronized, but internally call the concatenation methods of their AbstractStringBuilder parent.

Public StringBuilder appEnd (Object obj) {return appEnd (string.valueof (obj)); } public StringBuilder appEnd (String STR) {super.appEnd (STR); return this; } public StringBuilder appEnd (StringBuffer sb) {super.appEnd (sb); return this; } @override public StringBuilder appEnd (char[] STR) {super.append(STR); return this; Override public StringBuilder appEnd (Boolean b) {super.appEnd (b); return this; } public StringBuilder appEnd (char c) {super.appEnd (c); return this; }Copy the code

Abstractstringbuffer concatenation method AbstractStringBuilder concatenation method abstractStringBuffer concatenation method AbstractStringBuilder concatenation method

AbstractStringBuilder’s append() method

Inside the append() method, it basically ensures capacity (the ensure capacity method has a capacity expansion method) and appends.

public AbstractStringBuilder append(Object obj) { return append(String.valueOf(obj)); } 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; } public AbstractStringBuilder append(StringBuffer sb) { if (sb == null) return appendNull(); int len = sb.length(); ensureCapacityInternal(count + len); Sb.getchars (0, len, value, count); count += len; return this; } AbstractStringBuilder append(AbstractStringBuilder asb) { if (asb == null) return appendNull(); int len = asb.length(); ensureCapacityInternal(count + len); Asb. getChars(0, len, value, count); count += len; return this; } public AbstractStringBuilder append(boolean b) { if (b) { ensureCapacityInternal(count + 4); Value [count++] = 't'; value[count++] = 'r'; value[count++] = 'u'; value[count++] = 'e'; } else { ensureCapacityInternal(count + 5); Value [count++] = 'f'; value[count++] = 'a'; value[count++] = 'l'; value[count++] = 's'; value[count++] = 'e'; } return this; }Copy the code

AbstractStringBuilder expansion method

private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length Value = array.copyof (value, newCapacity(minimumCapacity)); } } private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; If (newCapacity -mincapacity < 0) {newCapacity = minCapacity; newCapacity = minCapacity; } the return (newCapacity < = 0 | | MAX_ARRAY_SIZE - newCapacity < 0) / / after the expansion of capacity is less than or equal to 0 or greater than the maximum capacity? HugeCapacity (minCapacity) // Execute hugeCapacity method: newCapacity; } private int hugeCapacity(int minCapacity) {if (integer. MAX_VALUE - minCapacity < 0) {overflow throw new OutOfMemoryError(); } return (minCapacity > MAX_ARRAY_SIZE) // The minimum required capacity is greater than the maximum capacity? minCapacity : MAX_ARRAY_SIZE; // The minimum required capacity is less than the maximum capacity returns the maximum capacity}Copy the code
Delete (int start, int end) method

Delete method of StringBuffer

Removes characters whose starting position starts with the subscript start and ends (excluding end)

@Override
    public synchronized StringBuffer delete(int start, int end) {
        toStringCache = null;
        super.delete(start, end);
        return this;
    }
Copy the code

Delete method of StringBuilder

Removes characters whose starting position starts with the subscript start and ends (excluding end)

@Override
public StringBuilder delete(int start, int end) {
    super.delete(start, end);
    return this;
}
Copy the code

Essentially, both invoke delete methods from their parent class AbstractBuilder

AbstractBuilder delete method

Public AbstractStringBuilder delete(int start, int end) {if (start < 0) Throw exceptions throw new StringIndexOutOfBoundsException (start); If (end > count) // If (end > count) end = count; If (start > end) / / start index greater than ending index, throw exceptions throw new StringIndexOutOfBoundsException (); int len = end - start; if (len > 0) { System.arraycopy(value, start+len, value, start, count-end); Arraycopy = len; arrayCopy = len; } return this; }Copy the code
Replace (int start, int end, String STR) method

Replace method of StringBuffer

@Override
public synchronized StringBuffer replace(int start, int end, String str) {
    toStringCache = null;
    super.replace(start, end, str);
    return this;
}
Copy the code

Replace method of StringBuilder

@Override
public StringBuilder replace(int start, int end, String str) {
    super.replace(start, end, str);
    return this;
}
Copy the code

Both call the replace method of the parent class AbstractBuilder

AbstractBuilderçš„replace(int start, int end, String str)

public AbstractStringBuilder replace(int start, int end, String str) { if (start < 0) throw new StringIndexOutOfBoundsException(start); if (start > count) throw new StringIndexOutOfBoundsException("start > length()"); if (start > end) throw new StringIndexOutOfBoundsException("start > end"); if (end > count) end = count; int len = str.length(); int newCount = count + len - (end - start); ensureCapacityInternal(newCount); System.arraycopy(value, end, value, start + len, count-end); Str.getchars (value, start); count = newCount; return this; }Copy the code
The toString method

ToString method of StringBuffer

Convert a StringBuffer object to a String

@Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value,  0, count); } return new String(toStringCache, true); }Copy the code

ToString method of StringBuilder

Convert StringBuilder to String

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}
Copy the code

conclusion

  • String is an immutable String, and the underlying char array is final. The advantage of String, using thread pools, can save space.

  • StringBuffer and StringBuilder are mutable strings (the underlying char array is of variable length). The main difference between the two is that both methods of StringBuffer are qualified with synchronized, indicating that StringBuffer is thread-safe. StringBuilder is thread-unsafe.

  • AbstractStringBuilder many of the methods inside StringBuff and StringBu are actually methods that call their parent class AbstractStringBuilder. It is recommended that StringBuilder and StringBuilder be used faster than String for frequent String concatenation.

  • conversion

    When a constructor is used, one can pass in either of the other two classes of objects (as well as objects of the same class)

    After being created, StringBuffer and StringBuilder are converted toString — > using the toString() method

    Although strings cannot be converted to StringBu or StringBuilder, strings can concatenate StringBuffer and StringBuider using the + sign

    A StringBuffer can be concatenated with a String or StringBuilder using the Append method

    A StringBuilder can concatenate a String or StringBuffer using the Append method