方法1:指向新的内存空间。即通过赋予新的实例的方式,使引用执行新的空的内存空间。
StringBuffer stringBuffer = new StringBuffer();
stringBuffer = new StringBuffer(); //通过赋值让引用执行新的内存空间
方法2:删除全部字符。即通过delete方法,从开始位删除到结尾位。
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Hello world!");
int length = stringBuffer.length();
stringBuffer.delete(0, length); // 删除stringBuffer保存的全部字符
方法3:设置长度为0。StringBuffer内部是通过字符数组的形式来保存内容的。StringBuffer有两个从父类AbstractStringBuilder中继承的成员属性,分别是用于存储内存的字符数组char[] value,和计算数组value中使用量的int count。设置长度为0就是通过setLength方法将count置零,让新传入的内容从value的0位置开始存储。
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Hello world!");
stringBuffer.setLength(0); //设置stringBuffer的长度为0
1、指向新的内存空间:
这种方式需要创建新的内存。从开销角度,是new一个实例的开销;但是从对JVM的影响的角度,丢弃原本的实例去执行新创建的实例的做法,会加速gc的到来,并不推荐。
2、删除全部字符:
查看源码:
@Override
public synchronized StringBuffer delete(int start, int end) {
toStringCache = null;
super.delete(start, end);
return this;
}
可以看到,StringBuffer的delete方法是通过调用父类的delete方法来实现的。查看父类AbstractStringBuilder的delete方法:
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
通过源码可以看出,传入合法的start和end参数的情况下,需要执行四次判断操作、一次减法和赋值操作,然后去调用System的arraycopy方法。查看System的arraycopy方法可以发现,这是一个本地方法。本地方法的调用,涉及到初次调用时本地方法的加载、java参数于本地方法参数的转换等的开销,以及由于JVM的GC无法管理本地方法内存,本地方法的使用者需要自行关注内存的创建、使用和销毁等问题,同时本地方法也无法享受JVM优化的成功。所以在涉及到本地方法调用的时候应该谨慎。System的arraycopy方法:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
3、设置长度为0:
查看setLength方法的源码:
@Override
public synchronized void setLength(int newLength) {
toStringCache = null;
super.setLength(newLength);
}
setLength方法也是通过调用父类的方法来实现的。继续去查看父类的代码:
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
ensureCapacityInternal(newLength);
if (count < newLength) {
Arrays.fill(value, count, newLength, '\0');
}
count = newLength;
}
根据我们的需要,输入参数应该设置为0,根据代码可知,会经历两次判断和一次赋值,操作明显比删除全部字符要简单。
总结:
从源码的角度,首选应该是使用setLength方法设置长度为0。
那么不妨用代码测试一下,是不是这种方法最快。
测试代码:
public class StringBufferClearer {
public static void main(String[] args) {
StringBuffer content = new StringBuffer();
for (int i = 0; i < 10; i++) {
content.append("Hello, world!");
}
String data = content.toString();
int count = 10000 * 10000;
// test way 1:
long timeBegin1 = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(data);
stringBuffer = new StringBuffer();
}
System.out.println("new StringBuffer() time cost: " + (System.currentTimeMillis() - timeBegin1));
// test way 2:
long timeBegin2 = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(data);
stringBuffer.delete(0, stringBuffer.length());
}
System.out.println("delete time cost: " + (System.currentTimeMillis() - timeBegin2));
// test way 3:
long timeBegin3 = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(data);
stringBuffer.setLength(0);
}
System.out.println("setLength time cost: " + (System.currentTimeMillis() - timeBegin3));
}
}
测试结果:
new StringBuffer() time cost: 6482
delete time cost: 5238
setLength time cost: 5096
根据测试结果,性能确实和预料基本相似。所以,推荐优先使用setLength方法设置长度为0。当然,delete方法开销的差距也并不大。