Is it better to reuse a StringBuilder in a loop?

I've a performance related question regarding use of StringBuilder. In a very long loop I'm manipulating a StringBuilder and passing it to another method like this:

for (loop condition) {
    StringBuilder sb = new StringBuilder();
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

Is instantiating StringBuilder at every loop cycle is a good solution? And is calling a delete instead better, like the following?

StringBuilder sb = new StringBuilder();
for (loop condition) {
    sb.delete(0, sb.length);
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

The second one is about 25% faster in my mini-benchmark.

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 );
    }
}

Results:

25265
17969

Note that this is with JRE 1.6.0_07.


Based on Jon Skeet's ideas in the edit, here's version 2. Same results though.

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 );
    }
}

Results:

5016
7516

In the philosophy of writing solid code its always better to put your StringBuilder inside your loop. This way it doesnt go outside the code its intended for.

Secondly the biggest improvment in StringBuilder comes from giving it an initial size to avoid it growing bigger while the loop runs

for (loop condition) {
  StringBuilder sb = new StringBuilder(4096);
}

Faster still:

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;
    }
}

In the philosophy of writing solid code, the inner workings of the method should be hidden from the objects that use the method. Thus it makes no difference from the system's perspective whether you redeclare the StringBuilder within the loop or outside of the loop. Since declaring it outside of the loop is faster, and it does not make the code more complicated to read, then reuse the object rather than reinstantiate it.

Even if the code was more complicated, and you knew for certain that object instantiation was the bottleneck, comment it.

Three runs with this answer:

$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570

Three runs with the other answer:

$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242

Although not significant, setting the StringBuilder 's initial buffer size will give a small gain.

链接地址: http://www.djcxy.com/p/72358.html

上一篇: 这在x86中有什么用处

下一篇: 在循环中重用StringBuilder会更好吗?