Очищення буфера / конструктора рядків після циклу


122

Як очистити буфер рядків у Java після циклу, щоб наступна ітерація використовувала чіткий буфер струна?


2
Інше питання: чому ви використовуєте SB лише для однієї ітерації циклу? Чи є ще одна внутрішня ітерація? SB не гідний, якщо ви просто хочете робити A + B + C + D (компілятор Java внутрішньо використовуватиме SB). Це допомагає, якщо ви хочете умовно додати частини рядків, але в іншому випадку ... просто використовуйте "+".
геліос

Відповіді:


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 ...
Jon

6
Я думаю, що користь від продуктивності походить від змінної строки, а не від збереження інстанції. ось короткий тест ітерацій 1e8: внутрішній цикл (2,97s): ideone.com/uyyTL14w , зовнішній цикл (2,87s): ideone.com/F9lgsIxh
Марк Елліот

6
Якщо говорити про ефективність: Якщо ви не отримаєте доступ до коду за багатопотоковим сценарієм, вам слід використовувати StringBuilder, а не StringBuffer - див. Javadoc: "Де можливо, рекомендується використовувати цей клас на користь StringBuffer, оскільки він буде швидше в більшість реалізацій ".
Рахель Люті

4
Єдина перевага створення СВ назовні - це не втрата внутрішнього (потенційно тривалого) характеру [] його. Якщо в першому ітераторі він виріс достатньо, у другому циклі не потрібно буде змінювати розмір будь-яких знаків []. Але для отримання переваги "чіткий метод" повинен буде зберегти розмір внутрішнього масиву. setLength робить це, але він також встановлює \ u0000 всі не використовувані символи в SB, тому це менш ефективно, ніж просто створити новий SB з хорошою початковою потужністю. Оголошення всередині циклу краще.
геліос

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)?
Томас Деко

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.
Луї CAD

1
Я погоджуюся, що в деяких випадках читабельність коду важливіша за продуктивність, але в цьому випадку це лише один рядок коду! І ви можете запустити тест, який доведе, що розподіл та створення об'єктів, а також збирання сміття дорожче, ніж це взагалі не робити. Оскільки ми знаходимося в циклі, це більш ніж актуально.
Луї CAD

1
Я також підписався на думку Кнута, але його точка зору не завжди відповідає дійсності. Додавання лише одного рядка для потенційного збільшення циклів процесора та пам'яті абсолютно не є злим. Ви ставитесь до ідеї занадто прямо. Зауважте, що цикл зазвичай повторюється багато разів. Цикл може повторюватися тисячі разів, який може споживати дорогоцінні мегабайти на мобільному телефоні (а також на сервері чи комп’ютері), якщо не ретельно оптимізований.
Луї CAD

1
Я думаю, що ми обидва висловили свої точки зору досить чітко, і я вважаю, що подальша дискусія не буде корисною для цього розділу коментарів. Якщо ви вважаєте, що моя відповідь є невірною, оманливою чи неповною, то ви - або хтось - будь-ласка, можете редагувати та / або спростовувати її.
Елі Ахеркан

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

Вартість StringBufferLength Часова вартість: 22878, 23017, 22894

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


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

Я думаю, що цей код швидше.


3
У цьому виникнуть проблеми з продуктивністю. Він створює новий об'єкт після кожної ітерації
Aditya Singh

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

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