Вопрос по stringbuilder, java – StringBuilder - сброс или создание нового

22

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

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

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

ИЛИ ЖЕ

stringBuilder=new StringBuilder();

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

@SotiriosDelimanolis Массив-копия для длины 0 для этого примера. Dukeling
Вы должны провести сравнительный анализ на основе вашего рабочего процесса приложения и выбрать то, что работает лучше для вас. Sanjay T. Sharma
Для справки,delete() ЗвонокSystem.arraycopy Sotirios Delimanolis
Я бы профилировал оба варианта, чтобы измерить производительность как памяти, так и использования процессора, и получить окончательный ответ для конкретного случая. Тем не менее, ИМО лучшим вариантом будетstringBuilder=new StringBuilder(); и пусть GC сделает свою работу. Luiggi Mendoza

Ваш Ответ

8   ответов
4

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

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

5

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

stringBuilder = new StringBuilder();

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

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

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

1

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

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

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

new StringBuilder () создает новый объект с массивом символов начальной емкости. Накладные расходы здесь: 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 (original, 0, copy, 0, Math.min (original.length, newLength)); возвратная копия; }

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

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

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

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
Да, это нужно расширить, но не уверен, почему это опасно. Вместо того, чтобы сохранять огромный буфер IMO, лучше расширять его по требованию. anubhava
Вы можете потерять память, используя эту технику. Если вы создаете все больше и больше строк, то меньшие, лежащие в основеchar[] станет больше, даже если вы неЯ использую все это. Sotirios Delimanolis
Я нене знаю, почему ты получил отрицательный голос. Я с тобой согласен. Sotirios Delimanolis
@SotiriosDelimanolis может быть прав, посмотрите на источник для:setLengthgrepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/...  В принципе,setLength просто обнуляет старые значения и нена самом деле уменьшить основной массив. Polaris878
0

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

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

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

1

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

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

stringBuilder.setLength(0);
1

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

 stringBuilder = new StringBuilder();

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

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

Ну, если тыЕсли вскоре снова использовать все / большинство из них, можно утверждать, что освобождать и перераспределять его не нужно. Dukeling
2

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

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

Похожие вопросы