在循环中重用 StringBuilder 是否更好?

2022-08-31 09:52:16

我有一个关于使用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());
}

答案 1

在我的迷你基准测试中,第二个速度提高了约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

答案 2

更快:

public class ScratchPad {

    private static String a;

    public static void main( String[] args ) throws Exception {
        final long time = System.currentTimeMillis();

        // Pre-allocate enough space to store all appended strings.
        // StringBuilder, ultimately, uses an array of characters.
        final 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( final 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