StringBuilder - сброс или создание нового

22

У меня есть условие, что StringBuilder сохраняет строки, соответствующие шаблону, из большого плоского файла (100 МБ). Однако после достижения условия я пишу содержимое varialble StringBuilder в текстовый файл.

Теперь я задаюсь вопросом, следует ли мне использовать одну и ту же переменную, сбросив объект - >

stringBuilder.delete(0,stringBuilder.length())

ИЛИ

stringBuilder=new StringBuilder();

Просьба предложить, какой из них, по вашему мнению, лучше, насколько это касается проблем производительности и OOM.

    
задан Chandru 12.09.2013 в 16:18
источник
  • Для справки, вызов delete () выполняет System.arraycopy. –  Sotirios Delimanolis 12.09.2013 в 16:20
  • Вы должны ориентироваться на основе рабочего процесса вашего приложения и выбирать, что лучше всего подходит для вас. –  Sanjay T. Sharma 12.09.2013 в 16:21
  • @SotiriosDelimanolis Arraycopy для длины 0 для этого примера. –  Dukeling 12.09.2013 в 16:30
  • Я бы проанализировал оба варианта для измерения производительности как памяти, так и использования процессора и получить окончательный ответ для конкретного случая. Тем не менее, ИМО лучшим вариантом будет stringBuilder = new StringBuilder (); и пусть GC выполняет свою работу. –  Luiggi Mendoza 12.09.2013 в 17:13

8 ответов

32

Я думаю, что StringBuilder#delete(start, end) по-прежнему дорогой вызов, вы должны сделать:

stringBuilder.setLength(0);

, чтобы сбросить его.

ОБНОВЛЕНИЕ: После просмотра исходный код StringBuilder Кажется, что setLength(int) оставляет старый буфер неповрежденным, и лучше позвонить: StringBuilder#trimToSize() после вызова выше, attempts to reduce storage used for the character sequence .

Итак, что-то вроде этого было бы более эффективным:

stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer
    
ответ дан anubhava 12.09.2013 в 16:22
  • Вы можете утечки памяти с помощью этой техники. Если вы создаете большие и большие строки, а затем более мелкие, основной символ [] станет больше, даже если вы не используете его все. –  Sotirios Delimanolis 12.09.2013 в 16:27
  • @anubhava Я не говорю о производительности. Метод setLength () просто устанавливает поле count в новую длину. Поэтому, если ваш char [] имеет размер 10_000_000, вы используете все это пространство ни для чего. –  Sotirios Delimanolis 12.09.2013 в 16:37
  • @anubhava Скажите, что у вас есть счет 100 для предыдущего использования, возможно, char [100], чтобы сопровождать это. Вызов setLength (0), где 100> 0, т.е. нет \ 0, это происходит только при увеличении емкости. Вместо этого выполняется else и счетчик равен 0. Таким образом, ссылка char [] не будет переназначена, и вы останетесь с символом [100], который вы не в полной мере используете. Даже если бы он установил все индексы символов в \ 0, вы все равно могли бы их расточать. –  Sotirios Delimanolis 12.09.2013 в 16:47
  • @anubhava trimToSize () также опасен, потому что он создаст символ [0], который нужно будет развернуть при первом добавлении (). –  Sotirios Delimanolis 12.09.2013 в 17:12
  • Я не знаю, почему вы получили вниз. Я с тобой согласен. –  Sotirios Delimanolis 13.09.2013 в 00:27
Показать остальные комментарии
5

Imho, я бы предложил использовать новое:

stringBuilder = new StringBuilder();

Я никогда не слышал об утечке памяти в StringBuilder, но когда вы действительно нажимаете ограничения, которые вы никогда не знаете. Я бы хеджировал свои ставки против него, каждый раз используя новый экземпляр.

В худшем случае, возможно, вы потеряете некоторую эффективность, и gc получает тренировку, но вы исключаете возможность OOM.

Из-за этого, а также по соображениям я бы лично принял новый подход.

    
ответ дан vikingsteve 12.09.2013 в 16:27
4

Одно фундаментальное различие заключается в том, что sb.delete сохраняет ссылку, а конструктор теряет ее.

Если ваш SB является аргументом метода и должен использоваться для передачи контента обратно вызывающему, вы должны использовать sb.delete. Вызывающий имеет исходную ссылку.

    
ответ дан user3241961 27.01.2014 в 21:02
2

Хорошо, есть большая разница между ними. Первый сохраняет все возможности, которые были у него до того, как вы удалили символы (т. Е. stringBuilder.capacity() ), тогда как второй создает новый StringBuilder с мощностью по умолчанию, 16. Конечно, вы можете просто передать stringBuilder.capacity() в качестве аргумента конструктору, но важно понять различие здесь, тем не менее.

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

    
ответ дан arshajii 12.09.2013 в 16:24
1

Я бы использовал:

 stringBuilder = new StringBuilder();

, потому что, если вы заполняете его большим количеством данных, вызов stringBuilder.setLength(0); не освободит массив подстановки, поэтому вы можете видеть, что использование памяти остается неоправданным.

Кроме того, это проще читать и понимать.

    
ответ дан Bohemian 12.09.2013 в 16:30
  • Хорошо, если вы собираетесь использовать все / большую часть его снова в ближайшее время, можно утверждать, что освобождение и перераспределение это необязательно. –  Dukeling 12.09.2013 в 16:33
1

В идеале мы должны использовать new StringBuilder() Копаем немного в классе StringBuilder из grepcode Я узнаю следующее.

Создание нового объекта:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

new StringBuilder () создает новый объект с начальным массивом char. Накладные расходы здесь: GC будет вызван для очистки старого объекта.

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

public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

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

public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }

Вызов copyOf из класса массива

public static char [] copyOf (char [] original, int newLength) {     char [] copy = new char [newLength];     System.arraycopy (оригинал, 0, копия, 0,                      Math.min (original.length, newLength));     возвратная копия; }

Теперь он также вызовет System.arrayCopy , который является родным методом. Теперь, если вы видите в copyOf, мы создаем новый charArray снова длиной 0, и когда мы попытаемся добавить к нему лишние данные, он будет вызывать расширение , потому что текущая длина будет 0 . Поэтому я думаю, что лучше назвать новый StringBuilder ()

Вы можете увидеть приведенный выше код на grepcode

PS: @ user3241961 записывается в том случае, если вы используете ссылку на этот объект, а затем новый должен будет снова установить его

    
ответ дан Akhil Dad 29.07.2015 в 15:11
1

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

Более быстрый способ:

stringBuilder.setLength(0);
    
ответ дан jmlotero 30.03.2017 в 09:51
0

Если вы находитесь в жестком цикле, и вы продолжите работу в этом цикле после того, как вы напишете данные в файл, вы должны обязательно повторно использовать StringBuilder. Нет причин не делать этого, и это лучше, чем вспенивание GC. Если вы пишете это на C или C ++, вы повторно используете буфер.

Кроме того, хотя true, что метод delete (...) вызывает System.arraycopy, количество копируемых байтов равно 0, поэтому оно незначительно.

Ah - кто-то еще сказал мне, что существует метод setLength (...), который является самым быстрым способом повторного использования буфера.

    
ответ дан jpayne 12.09.2013 в 16:37