一看指令码,这不是在循环里(if_icmpgt)给我 new 了 StringBuilder 了吗,怎么还这么慢呢?再仔细看,其实你会发现,这new是在循环里吗呀,我们把这段代码写出来再看看;
String str = ""; for (int i = 0; i < 10000; i++) { str = new StringBuilder().append(str).append(i).toString(); }
现在再看这段代码就很清晰了,所有的字符串链接操作,都需要实例化一次StringBuilder,所以非常耗时。「并且你可以验证,这样写代码耗时与字符串直接链接是一样的。」 所以把StringBuilder 提到上一层 for 循环外更快。
四、String 源码分析
publicfinalclassString implementsjava.io.Serializable, Comparable<String>, CharSequence{ /** The value is used for character storage. */ privatefinalchar value[];
/** Cache the hash code for the string */ privateint hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */ privatestaticfinallong serialVersionUID = -6849794470754667710L;
/** * Returns a canonical representation for the string object. * <p> * A pool of strings, initially empty, is maintained privately by the * class {@code String}. * <p> * When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. * <p> * It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. * <p> * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java™ Language Specification</cite>. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */ publicnative String intern();
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; returnthis; }
这个是 public final class StringBuilder extends AbstractStringBuilder,的父类与 StringBuffer 共用这个方法。
这里包括了容量检测、元素拷贝、记录 count 数量。
2.2 扩容操作
「ensureCapacityInternal(count + len);」
/** * This method has the same contract as ensureCapacity, but is * never synchronized. */ privatevoidensureCapacityInternal(int minimumCapacity){ // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); }
/** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ voidexpandCapacity(int minimumCapacity){ int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow thrownew OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
如上,StringBuilder,就跟操作数组的原理一样,都需要检测容量大小,按需扩容。扩容的容量是 n * 2 + 2,另外把原有元素拷贝到新新数组中。
2.3 填充元素
「str.getChars(0, len, value, count);」
publicvoidgetChars(int srcBegin, int srcEnd, char dst[], int dstBegin){ // ... System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }
添加元素的方式是基于 System.arraycopy 拷贝操作进行的,这是一个本地方法。
2.4 toString()
既然 stringBuilder 是数组,那么它是怎么转换成字符串的呢?
stringBuilder.toString();
@Override public String toString(){ // Create a copy, don't share the array returnnew String(value, 0, count); }