Очистка строкового буфера / построителя после цикла


122

Как очистить строковый буфер в Java после цикла, чтобы в следующей итерации использовался чистый строковый буфер?


2
Другой вопрос: почему вы используете SB только для одной итерации цикла? Есть ли еще одна внутренняя итерация? SB не подходит, если вы просто хотите сделать A + B + C + D (компилятор Java будет внутренне использовать SB). Это помогает, если вы хотите условно добавить части строк, но в противном случае ... просто используйте "+".
helios

Ответы:


135

Один из вариантов - использовать метод удаления следующим образом:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

Другой вариант (очиститель бит) использует setLength (int len) :

sb.setLength(0);

См. Javadoc для получения дополнительной информации:


15
Немного менее бессмысленным было бы просто объявить StringBuffer внутри цикла.
Марк Эллиот,

11
А, я думаю sb.setLength (0); чище и эффективнее, чем объявление внутри цикла. Ваше решение идет вразрез с преимуществом использования StringBuffer в производительности ...
Джон

6
Я думаю, что выигрыш в производительности достигается за счет изменчивости строки, а не за счет сохранения экземпляра. вот быстрый тест итераций 1e8: внутренний цикл (2,97 с): ideone.com/uyyTL14w , внешний цикл (2,87 с): ideone.com/F9lgsIxh
Марк Эллиот

6
Кстати о производительности: если к вашему коду не обращаются в многопоточном сценарии, вам следует использовать StringBuilder, а не StringBuffer - см. Javadoc: «По возможности рекомендуется использовать этот класс вместо StringBuffer, поскольку он будет быстрее в большинство реализаций ".
Рахель Люти,

4
Единственное преимущество создания SB снаружи - это не потерять его внутренний (потенциально длинный) char []. Если в первом итераторе он достаточно вырос, то во втором цикле не нужно будет изменять размер char []. Но для получения преимущества «чистый метод» должен сохранить размер внутреннего массива. setLength делает это, но также устанавливает в \ u0000 все неиспользуемые символы в SB, поэтому он менее эффективен, чем простое создание нового SB с хорошей начальной емкостью. Объявление внутри цикла лучше.
helios,

48

Самый простой способ повторно использовать StringBuffer- использовать методsetLength()

public void setLength(int newLength)

У вас может быть случай вроде

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb

2
@MMahmoud, в примере кода надо читать setLengthвместо setlength.
OnaBai

Когда я вижу исходный код setLength, как он работает ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ), здесь счетчик всегда будет больше, чем newLength (0)?
Thomas Decaux

20

У вас есть два варианта:

Либо используйте:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

Или используйте:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

НОТА

Избегайте объявления StringBufferили StringBuilderобъектов внутри цикла, иначе он будет создавать новые объекты с каждой итерацией. Создание объектов требует системных ресурсов, места, а также времени. Поэтому в долгосрочной перспективе избегайте объявления их в цикле, если это возможно.



5

Я предлагаю создавать новый StringBuffer(или даже лучше StringBuilder) для каждой итерации. Разница в производительности действительно незначительна, но ваш код будет короче и проще.


2
Если у вас есть доказательства такой разницы в производительности, поделитесь ими. (Я также буду рад увидеть статистику того, где в основном используется Java, и под каким определением «в основном».)
Эли Ахеркан,

1
Хорошо известно, что выделение памяти (новое ключевое слово в Java) дороже, чем простое изменение некоторых полей одного и того же объекта. Что касается «в основном», на которое вы могли бы заменить слово «высоко», существует более 1,5 миллиарда Android-устройств, и все они используют Java.
Louis CAD

1
Я согласен с тем, что в некоторых случаях читаемость кода важнее производительности, но в данном случае это всего лишь одна строка кода! И вы можете запустить тест, который докажет вам, что выделение и создание объектов, а также сборка мусора обходятся дороже, чем не делать этого вообще. Поскольку мы в курсе, это более чем актуально.
Луи CAD

1
Я также присоединился к точке зрения Кнута, но его точка зрения не всегда верна. Добавление всего одной строчки для потенциального увеличения количества циклов процессора и памяти - это абсолютно не зло. Вы слишком прямолинейно понимаете идею. Обратите внимание, что цикл обычно повторяется много раз. Цикл может повторяться тысячи раз, что может съесть драгоценные мегабайты на мобильном телефоне (а также на сервере или компьютере), если его не оптимизировать тщательно.
Louis CAD

1
Я думаю, что мы оба достаточно ясно изложили нашу точку зрения, и я считаю, что дальнейшее обсуждение не принесет пользы для этого раздела комментариев. Если вы считаете, что мой ответ неверен, вводит в заблуждение или является неполным, то вы - или кто-либо другой - можете отредактировать его и / или проголосовать против него.
Eli Acherkan

5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Использование:

StringBuilder v = new StringBuilder();
clear(v);

по удобочитаемости считаю это лучшим решением.


2

Уже есть хороший ответ. Просто добавьте результат теста для разницы в производительности StringBuffer и StringBuild, используйте новый экземпляр в цикле или используйте setLength (0) в цикле.

Резюме: в большом цикле

  • StringBuilder намного быстрее StringBuffer
  • Создание нового экземпляра StringBuilder в цикле не имеет разницы с setLength (0). (setLength (0) имеет очень-очень-очень маленькое преимущество, чем создание нового экземпляра.)
  • StringBuffer медленнее, чем StringBuilder, создавая новый экземпляр в цикле
  • setLength (0) из StringBuffer работает намного медленнее, чем создание нового экземпляра в цикле.

Очень простой тест (я просто вручную изменил код и провожу другой тест):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

Новый экземпляр StringBuilder в цикле: временные затраты: 3693, 3862, 3624, 3742

StringBuilder setLength: Стоимость времени: 3465, 3421, 3557, 3408

Новый экземпляр StringBuffer в цикле: временные затраты: 8327, 8324, 8284

StringBuffer setLength Стоимость времени: 22878, 23017, 22894

Снова StringBuilder setLength, чтобы убедиться, что у моей лаборатории не возникла проблема с использованием такой длины для StringBuffer setLength :-) Затраты времени: 3448


0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

я думаю, что этот код быстрее.


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

@AdityaSingh Это вполне разумный подход. Не допускайте проблем с производительностью без доказательств.
shmosel

Предположение, основанное на понимании происходящего, является основой интеллекта.
rghome
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.