- Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
- This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
preface
Because the interviewer asked, “How does StringBuffer implement Append?”
Just sketch the idea
So I’m thinking of dynamic arrays wow, but I’m thinking of not having to new an array every time;
You don’t have to move the string array around too often.
Basic description is OK ~ but look at the source no problem
DKDK Go, go, go
The debug process is interesting
The article from my interview experience: blog.csdn.net/pmdream/art… To join the challenge, you can write original articles from other blogs
The source code
append
@Override public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; } @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } /** * Appends the specified {@code StringBuffer} to this sequence. * <p> * The characters of the {@code StringBuffer} argument are appended, * in order, to the contents of this {@code StringBuffer}, increasing the * length of this {@code StringBuffer} by the length of the argument. * If {@code sb} is {@code null}, then the four characters * {@code "null"} are appended to this {@code StringBuffer}. * <p> * Let <i>n</i> be the length of the old character sequence, the one * contained in the {@code StringBuffer} just prior to execution of the * {@code append} method. Then the character at index <i>k</i> in * the new character sequence is equal to the character at index <i>k</i> * in the old character sequence, if <i>k</i> is less than <i>n</i>; * otherwise, it is equal to the character at index <i>k-n</i> in the * argument {@code sb}. * <p> * This method synchronizes on {@code this}, the destination * object, but does not synchronize on the source ({@code sb}). * * @param sb the {@code StringBuffer} to append. * @return a Reference to this object. * @since 1.4 */ public synchronized StringBuffer append(StringBuffer sb) {toStringCache = null; super.append(sb); return this; } /** * @override synchronized StringBuffer append(AbstractStringBuilder asb) {toStringCache = null; super.append(asb); return this; } /** * Appends the specified {@code CharSequence} to this * sequence. * <p> * The characters of the {@code CharSequence} argument are appended, * in order, increasing the length of this sequence by the length of the * argument. * * <p>The result of this method is exactly the same as if it were an * invocation of this.append(s, 0, s.length()); * * <p>This method synchronizes on {@code this}, the destination * object, but does not synchronize on the source ({@code s}). * * <p>If {@code s} is {@code null}, then the four characters * {@code "null"} are appended. * * @param s the {@code CharSequence} to append. * @return a Reference to this object. * @override public synchronized StringBuffer appEnd (CharSequence s) { toStringCache = null; super.append(s); return this; } / * * * @ throws IndexOutOfBoundsException {@ inheritDoc} * * / @ @ since 1.5 Override public synchronized StringBuffer append(CharSequence s, int start, int end) { toStringCache = null; super.append(s, start, end); return this; } @Override public synchronized StringBuffer append(char[] str) { toStringCache = null; super.append(str); return this; } /** * @throws IndexOutOfBoundsException {@inheritDoc} */ @Override public synchronized StringBuffer append(char[] str, int offset, int len) { toStringCache = null; super.append(str, offset, len); 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; } @Override public synchronized StringBuffer append(int i) { toStringCache = null; super.append(i); return this; }Copy the code
Whatever append is,
Super.append (obj);
Note that these are thread safe synchronized modifications
toStringCache
Cleared whenever the StringBuffer is modified. So if I call Append, this thing is going to be emptied, which is equivalent to a cache from the last time I called toString;
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
Copy the code
// It’s ok to write this, StringBuffer can accept strings, numbers, etc
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append(1);
System.out.println(sb.toString());
sb.append("c");
System.out.println(sb.toString());
}
Copy the code
Append number source code
/**
* Appends the string representation of the {@code int}
* argument to this sequence.
* <p>
* The overall effect is exactly as if the argument were converted
* to a string by the method {@link String#valueOf(int)},
* and the characters of that string were then
* {@link #append(String) appended} to this character sequence.
*
* @param i an {@code int}.
* @return a reference to this object.
*/
public AbstractStringBuilder append(int i) {
if (i == Integer.MIN_VALUE) {
append("-2147483648");
return this;
}
int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
: Integer.stringSize(i);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Integer.getChars(i, spaceNeeded, value);
count = spaceNeeded;
return this;
}
Copy the code
Append string source
/** * Appends the specified string to this character sequence. * <p> * The characters of the {@code String} argument are appended, in * order, increasing the length of this sequence by the length of the * argument. If {@code str} is {@code null}, then the four * characters {@code "null"} are appended. * <p> * Let <i>n</i> be the length of this character sequence just prior to * execution of the {@code append} method. Then the character at * index <i>k</i> in the new character sequence is equal to the character * at index <i>k</i> in the old character sequence, if <i>k</i> is less * than <i>n</i>; otherwise, it is equal to the character at index * <i>k-n</i> in the argument {@code str}. * * @param str a string. * @return a reference to this object. */ 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; }Copy the code
Compare the two codes above, mainly Enrecapact Internal and getChars
ensureCapacityInternal
The length that’s passed in is actually the length of the string that’s added
/** * For positive values of {@code minimumCapacity}, this method * behaves like {@code ensureCapacity}, however it is never * synchronized. * If {@code minimumCapacity} is non positive due to numeric * overflow, this method throws {@code OutOfMemoryError}. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); }}Copy the code
// Add the old array to the new array, use the new length, enlarge the length to the passed parameter length, and reassign the value to value
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
Copy the code
The newCapacity calculation function newCapacity
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by the same amount + 2 if
* that suffices.
* Will not return a capacity greater than {@code MAX_ARRAY_SIZE}
* unless the given minimum capacity is greater than that.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero or
* greater than Integer.MAX_VALUE
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
Copy the code
How does this work out?
Value. Length is initially allocated 16 characters because the string is hello.
X <<1 corresponds to moving the integer stored in x one bit to the left (equivalent to multiplying by 2)
So (16 << 1) +2 = 34
Int newCapacity = (value.length << 1) + 2
The debug process
The original string is hello, append world
You can see that there is a cache
This length is the sum of the length, the smallest length
If the value from 10-16 is not greater than 0, you do not need to expand the value.
GetChars is just the beginning and end of the original string, and then the expanded array of characters (maybe not expanded), and then where does the new one start
The value of this execution is world: and then copy the string
Add the current value, and the string length becomes 10