StringBuilder
Строку изменить нельзя. А если текст нужно собирать по кусочку, например в цикле? Каждый + создаёт новую строку:
public class Main {
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 5; i++) {
s = s + i;
}
System.out.println(s); // 01234
}
}Программа работает и печатает 01234. Но посмотрим, что происходит на каждом круге цикла:
- На первом круге
s + iсоздаёт новую строку0. - На втором — новую строку
01: в неё копируется старый символ и дописывается новый. - На третьем — строку
012, и снова копируются все прежние символы. - И так до конца: каждый круг делает новую карточку и переписывает на неё весь накопленный текст.
Если каждый круг добавляет один символ, то на сотом круге Java переписывает 100 символов, а на десятитысячном — 10 000. За 10 000 кругов набегает 1 + 2 + … + 10 000, то есть около 50 миллионов символов, хотя в готовой строке их всего 10 000. Чем длиннее текст, тем дороже каждый следующий круг. Почему + в цикле работает медленно — частый вопрос на собеседовании.
Представьте черновик. На карточке ничего не исправишь, а на черновике можно дописывать, вставлять и стирать сколько угодно. Когда текст готов, его переписывают начисто на карточку. В Java такой черновик называется StringBuilder.
Черновик StringBuilder
StringBuilder хранит изменяемую последовательность символов. Его методы меняют сам черновик и не создают новый:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
sb.append(i);
}
String result = sb.toString();
System.out.println(result); // 01234Разберём по строкам:
new StringBuilder()создаёт пустой черновик. Словоnewсоздаёт новый объект.sb.append(i)дописывает число в конец черновика. Новая строка при этом не создаётся.sb.toString()переписывает текст начисто: возвращает обычную строкуString.println(result)печатает01234.
Когда текст перестаёт помещаться, черновик сам берёт себе место с запасом. Поэтому обычно круг дописывает только новый символ, а весь текст переносится изредка — когда черновик растёт.
Здесь картинка с черновиком неточна. Бумажный черновик после переписывания начисто обычно выбрасывают. StringBuilder после toString() никуда не девается: его можно менять дальше, а уже готовая строка от этого не изменится.
Методы черновика
StringBuilder sb = new StringBuilder("Java");
sb.append("!").insert(0, "Hi, ");
System.out.println(sb); // Hi, Java!
System.out.println(sb.length()); // 9
sb.reverse();
System.out.println(sb); // !avaJ ,iHРазберём методы по одному:
append("!")дописывает значение в конец:Java!;insert(0, "Hi, ")вставляет текст перед индексом0, то есть в начало:Hi, Java!;length()возвращает длину, как у строки;reverse()переворачивает текст задом наперёд.
Обратите внимание на две вещи:
append,insertиreverseвозвращают тот же черновик. Поэтому вызовы можно писать цепочкой: вsb.append("!").insert(0, "Hi, ")сначала выполняетсяappend, потомinsertу того жеsb. Цепочка выполняется слева направо.sb.reverse();меняетsbбез всякого присваивания. Метод строки вернул бы новую строку, а методStringBuilderменяет сам черновик.
println(sb) печатает текст черновика: println сам вызывает у него toString().
StringBuilder или +
Когда что выбирать:
- склейку в одном выражении, например
"Hi, " + name + "!", оставьте на+: Java собирает такую строку за один раз; - текст, который собирают в цикле, собирайте в
StringBuilder.
На собеседовании к этому вопросу часто добавляют второй: чем StringBuilder отличается от StringBuffer. Методы у них одинаковые. Но StringBuffer защищён на случай, когда с ним одновременно работают несколько потоков — частей программы, которые выполняются параллельно. Эта защита стоит скорости, поэтому в обычном коде берут StringBuilder.
Частая ошибка
Черновик положили в переменную типа String:
StringBuilder sb = new StringBuilder("Hi");
sb.append("!");
String text = sb; // ✗error: incompatible types: StringBuilder cannot be converted to StringПрограмма не компилируется: StringBuilder и String — разные типы, и черновик сам строкой не становится. Перепишите его начисто: String text = sb.toString();.
Главное
+в цикле каждый раз создаёт новую строку и копирует весь накопленный текст.StringBuilder— изменяемый черновик:append,insertиreverseменяют его на месте и возвращают его же.- Вызовы методов
StringBuilderможно писать цепочкой; они выполняются слева направо. - Готовую строку получают через
toString(); в обычном коде берутStringBuilder, а неStringBuffer.