人们常常认为避免创建对象(特别是在循环中)被认为是一种很好的做法。
那么,关于StringBuffer
最有效的是什么?
StringBuffer sb = new StringBuffer();
ObjectInputStream ois = ...;
for (int i=0;i<1000;i++) {
for (j=0;i<10;j++) {
sb.append(ois.readUTF());
}
...
// Which option is the most efficient?
sb = new StringBuffer(); // new StringBuffer instance?
sb.delete(0,sb.length()); // or deleting content?
}
我的意思是,有人可能认为创建一个对象比循环遍历数组要快。
答案 0 :(得分:16)
首先StringBuffer
是线程安全的,与StringBuilder
相比,性能会有所下降。 StringBuilder不是线程安全的,但结果更快。最后,我更喜欢使用setLength将长度设置为0。
sb.setLength(0)
这类似于.delete(...)
,除非你真的不关心长度。也可能快一点,因为它不需要'删除'任何东西。创建新的StringBuilder
(或StringBuffer)效率会降低。任何时候你看到new
Java正在创建一个新对象并将其放在堆上。
注意:在查看.delete
和.setLength
的实施后,.delete
设置长度= 0,而.setLength
将所有内容设置为'\0'
所以你可以通过.delete
获得一点胜利
答案 1 :(得分:3)
只是为了放大以前的评论:
从查看源代码,delete
()总是调用System.arraycopy
(),但如果参数是(0,count),它将调用arraycopy
(),其长度为零,这可能没有效果。恕我直言,这应该进行优化,因为我敢打赌这是最常见的情况,但无论如何。
另一方面,对于setLength
(),调用将通过调用ensureCapacityInternal
()来增加StringBuilder的容量(另一个应该已经优化的常见情况恕我直言)然后截断delete
()所需的长度。
最终,这两种方法最终都将count
设置为零。
在这种情况下,两个调用都没有进行任何迭代。两者都进行不必要的函数调用。但是,ensureCapacityInternal()是一个非常简单的私有方法,它邀请编译器几乎不存在优化它,因此setLength
()可能稍微更高效。
我非常怀疑创建一个新的StringBuilder实例可能会像将count
设置为零一样高效,但我认为编译器可能会识别所涉及的模式将重复的实例化转换为对setLength
(0)的重复调用。但在最好的情况下,这将是一个洗涤。而且你依赖于编译器来识别这种情况。
执行摘要:setLength(0)是最有效的。为了获得最大效率,请在创建时在StringBuilder中预先分配缓冲区空间。
答案 2 :(得分:1)
删除方法以这种方式实现:
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;
}
正如您所看到的,它不会遍历数组。