1. Неизменяемость строки: друг или враг?
В Java класс String — неизменяемый (immutable). Это значит, что после создания строку нельзя изменить. Каждый раз, когда вы "изменяете" строку, например, добавляете к ней что-то через + или concat(), на самом деле создаётся новый объект, а старый отправляется на свалку истории (и памяти).
Пример:
String s = "Hello";
s = s + " world!";
System.out.println(s); // Hello world!
Выглядит, будто строка s изменилась, но на самом деле создалась новая строка "Hello world!", а старая "Hello" осталась в памяти до тех пор, пока её не уберёт сборщик мусора. Если таких операций много — например, в цикле, — программа начинает тормозить и использовать лишнюю память.
Представьте, что вы строите башню из кубиков, и каждый раз, когда хотите добавить новый кубик, вам приходится перестраивать всю башню заново. Не очень экономно, правда? Вот так же работает обычный String в Java при частых изменениях.
2. StringBuilder: быстрый "строитель" строк
Класс StringBuilder (пакет java.lang, импортировать не нужно) — это специальный инструмент для эффективной сборки и изменения строк. Он изменяемый (mutable): можно добавлять, удалять, вставлять символы и подстроки без создания нового объекта на каждую операцию.
Аналогия:
Если String — это бетонная плита, то StringBuilder — конструктор LEGO: добавляете и убираете детали сколько угодно, не разбирая всё заново.
Как создать StringBuilder
StringBuilder sb = new StringBuilder(); // пустой
StringBuilder sb2 = new StringBuilder("Начальное значение");
Основные методы
| Метод | Описание | Пример использования |
|---|---|---|
|
Добавить в конец строку, число, символ и т.д. | |
|
Вставить значение в указанную позицию | |
|
Удалить символы с позиции start (включ.) до end (не включ.) | |
|
Заменить часть строки другим содержимым | |
|
Развернуть строку задом наперёд | |
|
Преобразовать в обычную строку | |
|
Обрезать или дополнить строку до заданной длины | |
Пример использования
StringBuilder sb = new StringBuilder();
sb.append("Привет, ");
sb.append("мир!");
System.out.println(sb); // Привет, мир!
sb.insert(7, "Java "); // вставим "Java " после "Привет, "
System.out.println(sb); // Привет, Java мир!
sb.replace(8, 12, "другой"); // заменим "Java" на "другой"
System.out.println(sb); // Привет, другой мир!
sb.reverse();
System.out.println(sb); // !рим йогурд ,тевирП
3. StringBuffer: старший брат с защитой от многопоточности
В чём разница между StringBuilder и StringBuffer?
- StringBuilder — быстрый, но не потокобезопасный (не синхронизированный).
- StringBuffer — медленнее, но потокобезопасный (синхронизированный).
Если ваше приложение работает в одном потоке — используйте StringBuilder (быстрее). Если несколько потоков могут менять одну и ту же строку — используйте StringBuffer.
4. Когда использовать StringBuilder вместо String?
Сценарии
- Частые изменения строки (добавление, удаление, вставка) в цикле или при сборке больших текстов.
- Сборка строки из массива/списка (CSV, HTML, отчёты и т.д.).
- Парсинг и обработка текста с большим числом операций над строкой.
Пример: сборка строки из массива
Плохой способ (через String и +):
String[] names = {"Иван", "Пётр", "Мария"};
String result = "";
for (int i = 0; i < names.length; i++)
{
result += names[i];
if (i < names.length - 1)
{
result += ", ";
}
}
System.out.println(result);
Хороший способ (через StringBuilder):
String[] names = {"Иван", "Пётр", "Мария"};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < names.length; i++)
{
sb.append(names[i]);
if (i < names.length - 1)
{
sb.append(", ");
}
}
System.out.println(sb.toString());
Разница: в первом случае на каждом шаге создаётся новая строка, во втором — мы наращиваем один объект StringBuilder.
5. Сравнение производительности: String vs StringBuilder
// Через String
long t1 = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 10000; i++)
{
s += i + " ";
}
long t2 = System.currentTimeMillis();
System.out.println("String: " + (t2 - t1) + " мс");
// Через StringBuilder
t1 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.append(i).append(" ");
}
s = sb.toString();
t2 = System.currentTimeMillis();
System.out.println("StringBuilder: " + (t2 - t1) + " мс");
Итог: в большинстве случаев StringBuilder работает на порядки быстрее при множественных конкатенациях.
6. Полезные нюансы
Можно ли использовать методы String?
У StringBuilder свои методы. Чтобы получить строку — вызывайте toString().
StringBuilder sb = new StringBuilder("Hello");
String s = sb.toString(); // теперь s — обычная строка
Можно ли сравнивать StringBuilder через equals?
Осторожно: sb1.equals(sb2) сравнивает ссылки, а не содержимое. Сравнивайте так:
if (sb1.toString().equals(sb2.toString()))
{
// содержимое совпадает
}
Можно ли передать StringBuilder в System.out.println?
Да. System.out.println вызовет toString() автоматически.
Можно ли читать символы по индексу?
Да, используйте charAt(int index), как у обычной строки.
7. Типичные ошибки при работе с StringBuilder и StringBuffer
Ошибка №1: сравнивать два StringBuilder через equals или оператор ==. Эти проверки сравнивают ссылки, а не содержимое. Используйте toString() и сравнение строк.
Ошибка №2: забывать вызывать toString() там, где ожидается String (возврат из метода, логирование, передача в API).
Ошибка №3: применять StringBuilder для пары простых конкатенаций. Запись вида "Hello, " + name нормально читается и эффективна.
Ошибка №4: конкатенировать String в цикле через +. Это неэффективно по времени и памяти — используйте StringBuilder.
Ошибка №5: путать StringBuffer и StringBuilder без необходимости. Если нет совместной модификации из нескольких потоков — выбирайте StringBuilder.
Ошибка №6: бездумно обрезать содержимое StringBuilder методом setLength(). Проверяйте корректность новой длины, данные после неё будут утеряны.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ