在循环中重用StringBuilder会更好吗?
我有一个关于使用StringBuilder的性能相关问题。 在一个很长的循环中,我操纵一个StringBuilder
并将它传递给另一个方法,如下所示:
for (loop condition) {
StringBuilder sb = new StringBuilder();
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
在每个循环周期实例化StringBuilder
是一个很好的解决方案吗? 并且更好地调用删除,如下所示?
StringBuilder sb = new StringBuilder();
for (loop condition) {
sb.delete(0, sb.length);
sb.append("some string");
. . .
sb.append(anotherString);
. . .
passToMethod(sb.toString());
}
第二个是我的迷你基准测试中快25%左右。
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb = new StringBuilder();
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
结果:
25265
17969
请注意,这是JRE 1.6.0_07。
根据Jon Skeet在编辑中的想法,这里是第2版。虽然结果相同。
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb2 = new StringBuilder();
sb2.append( "someString" );
sb2.append( "someString2" );
sb2.append( "someStrin4g" );
sb2.append( "someStr5ing" );
sb2.append( "someSt7ring" );
a = sb2.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
结果:
5016
7516
在编写可靠代码的哲学中,最好将您的StringBuilder放入循环中。 这样它就不会超出其预期的代码范围。
其次,在StringBuilder中最大的改进来自给它一个初始大小,以避免它在循环运行时变大
for (loop condition) {
StringBuilder sb = new StringBuilder(4096);
}
更快:
public class ScratchPad {
private static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder( 128 );
for( int i = 0; i < 10000000; i++ ) {
// Resetting the string is faster than creating a new object.
// Since this is a critical loop, every instruction counts.
//
sb.setLength( 0 );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
setA( sb.toString() );
}
System.out.println( System.currentTimeMillis()-time );
}
private static void setA( String aString ) {
a = aString;
}
}
在编写可靠代码的哲学中,方法的内部工作应该从使用该方法的对象中隐藏起来。 因此,无论是在循环内还是在循环外重新声明StringBuilder,系统的角度都没有什么不同。 由于在循环之外声明它更快,并且不会使代码更加复杂,因此请重用该对象而不是重新实例化它。
即使代码更复杂,并且您肯定知道对象实例化是瓶颈,请对其进行评论。
三个运行这个答案:
$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570
三个运行与其他答案:
$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242
虽然不显着,但设置StringBuilder
的初始缓冲区大小将会带来一点收益。