平时我们一般涉及到字符串的拼接,最本能最直接的方法就是+.
String s1 = "Hello";
s1=s1+"word";
当数据拼接次数比较少的时候,这样的方法无可厚非,但是一旦次数较多时,这样的方法效率很低,而且浪费大量的空间
现在我要讲的是String,StringBuilder,StringBuffer,这三个有什么区别,以及平时我们使用时该注意什么?
1.String
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。
为什么会浪费大量的空间?
看上面的代码,假设s1指向地址0x0001 ,当重新赋值后s1指向的地址是0x0002了,然而地址0x0001里保存的s1依然存在,这就导致了每次重新赋值,就会产生一个新的地址指向(新的对象),而原来的不会消失,浪费了大量的空间,因此String的操作都是改变赋值地址而不是改变值操作,最后,原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会有多底。
除此之外字符串拼接还有str.concat("test");方法,这个重新赋值也会产生新的对象,但效率会比+高一点。
2.StringBuffer
StringBuffer是可变类和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
看上面的源码我们会发现
StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区
StringBuffer buf=new StringBuffer("this"); //分配长"this".length()+16字节的字符缓冲区
StringBuffer buf=new StringBuffer("this")//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。
那么字符串大小超过容量时是怎么扩容的呢?
这可能就涉及到其常用的append方法了
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
// Documentation in subclasses because of synchro difference
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;
}
/**
* 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));
}
}
/**
* 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;(value.length << 1表示原来的2倍,这个是二进制写法后的转换问题)
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
看上面的源码会发现扩容到原来的两倍+2的长度,如果还是不够的话就用到了minimumCapacity=count+len这个长度
3.StringBuilder
StringBuilder 类在 Java 5中被提出来的,相对于上面的StringBuffer,但这个不是线程安全的(不能同步访问),由于它不执行同步,StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。