Лучше ли повторно использовать StringBuilder в цикле? - PullRequest
96 голосов
/ 28 октября 2008

У меня есть вопрос, связанный с производительностью относительно использования 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());
}

Ответы [ 14 ]

1 голос
/ 16 июля 2009

Причиной того, что выполнение setLength или delete повышает производительность, является, главным образом, код, «изучающий» правильный размер буфера и меньше занимающий выделение памяти. Как правило, Я рекомендую позволить компилятору оптимизировать строки . Однако, если производительность критична, я часто заранее вычисляю ожидаемый размер буфера. Размер StringBuilder по умолчанию составляет 16 символов. Если вы вырастете за пределы этого, то оно должно измениться. Изменение размера - это когда производительность теряется. Вот еще один мини-тест, который иллюстрирует это:

private void clear() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;
    StringBuilder sb = new StringBuilder();

    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" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Clear buffer: " + (System.currentTimeMillis()-time) );
}

private void preAllocate() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;

    for( int i = 0; i < 10000000; i++ ) {
        StringBuilder sb = new StringBuilder(82);
        sb.append( "someString" );
        sb.append( "someString2" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Pre allocate: " + (System.currentTimeMillis()-time) );
}

public void testBoth() throws Exception {
    for(int i = 0; i < 5; i++) {
        clear();
        preAllocate();
    }
}

Результаты показывают, что повторное использование объекта примерно на 10% быстрее, чем создание буфера ожидаемого размера.

0 голосов
/ 30 октября 2008

Первый лучше для человека. Если второе работает немного быстрее в некоторых версиях некоторых JVM, то что?

Если производительность настолько критична, обходите StringBuilder и пишите свой собственный. Если вы хороший программист и учитываете, как ваше приложение использует эту функцию, вы сможете сделать ее еще быстрее. Стоящий? Вероятно, нет.

Почему этот вопрос рассматривается как "любимый вопрос"? Потому что оптимизация производительности - это очень весело, независимо от того, практична она или нет.

0 голосов
/ 28 октября 2008

Объявляйте один раз и присваивайте каждый раз. Это более прагматичная и многократно используемая концепция, чем оптимизация.

0 голосов
/ 28 октября 2008

Увы, я обычно на C #, думаю, что очистка StringBuilder весит больше, чем создание нового.

...