Как очистить StringBuilder в Java: правильные способы и подводные камни
Основные способы очистки StringBuilder
Существует несколько подходов, но не все они одинаково эффективны. Рассмотрим каждый подробно.
1. Метод setLength(int newLength) — рекомендуемый способ
Вызов sb.setLength(0) — самый быстрый и правильный способ очистки StringBuilder. Этот метод не создает новый объект и не копирует данные, а просто устанавливает внутренний счетчик длины в 0, помечая все символы как "неиспользуемые". Буфер при этом остается выделенным и может быть переиспользован.
StringBuilder sb = new StringBuilder("Привет, мир!");
sb.append(" Как дела?");
System.out.println(sb); // Привет, мир! Как дела?
// Очищаем
sb.setLength(0);
System.out.println("Длина: " + sb.length()); // 0
System.out.println("Емкость: " + sb.capacity()); // Емкость сохраняется
// Можно сразу использовать снова
sb.append("Новая строка");
System.out.println(sb); // Новая строка
2. Метод delete(int start, int end)
Альтернативный способ: sb.delete(0, sb.length()). Он тоже работает, но немного медленнее, чем setLength(0), так как внутри происходит сдвиг данных (хотя в данном случае сдвигать нечего). На практике разница незаметна для большинства приложений, но в высоконагруженных циклах лучше использовать setLength.
StringBuilder sb = new StringBuilder("Тестовая строка");
sb.delete(0, sb.length());
System.out.println("Длина после delete: " + sb.length()); // 0
3. Создание нового объекта — НЕ рекомендуется
sb = new StringBuilder() — самый понятный для новичков способ, но он не очищает старый объект, а создает новый. Старый StringBuilder остается в памяти до сборки мусора. В цикле с миллионами итераций это приведет к:
- Увеличению нагрузки на GC (Garbage Collector)
- Потере производительности из-за постоянного выделения памяти
- Фрагментации памяти
4. Важный нюанс: метод toString() и ссылки
Если вы вызвали sb.toString() и сохранили результат, а затем очистили StringBuilder, сохраненная строка не изменится — строки в Java immutable (неизменяемые). Однако есть подводный камень: до Java 7u6 метод toString() возвращал подстроку, которая ссылалась на внутренний массив символов StringBuilder. Это могло привести к утечкам памяти. В современных версиях Java эта проблема исправлена.
StringBuilder sb = new StringBuilder("Важные данные");
String saved = sb.toString(); // Сохраняем копию
sb.setLength(0); // Очищаем
System.out.println(saved); // "Важные данные" — строка не изменилась
Сравнение производительности
Бенчмарки показывают, что setLength(0) быстрее delete() примерно на 10-15% в циклах. Создание нового объекта может быть медленнее в 2-5 раз из-за аллокации памяти и работы GC.
Практический пример: обработка CSV-строк
public List<String> parseCsvLines(String[] lines) {
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder(256); // Оптимальный начальный размер
for (String line : lines) {
String[] parts = line.split(",");
for (String part : parts) {
sb.append(part.trim());
sb.append("|");
}
if (sb.length() > 0) {
sb.setLength(sb.length() - 1); // Убираем последний разделитель
}
result.add(sb.toString());
sb.setLength(0); // Очищаем для следующей строки
}
return result;
}
Вывод: используйте sb.setLength(0) для очистки StringBuilder. Это эффективно, читаемо и не создает лишних объектов. Избегайте создания нового объекта в циклах, если это не критично для логики приложения.
Дополнительный совет: метод delete() с проверкой длины
Если вы предпочитаете более явный синтаксис, можно использовать delete(), но с небольшой оптимизацией — проверять, не пуст ли StringBuilder перед очисткой:
public static void clearBuilder(StringBuilder sb) {
if (sb != null && sb.length() > 0) {
sb.delete(0, sb.length());
}
}
Этот подход полезен, когда вы не уверены, что в билдере есть данные, и хотите избежать лишних операций.
Когда создание нового объекта оправдано?
Есть сценарии, где new StringBuilder() может быть предпочтительнее:
- Когда старый StringBuilder имеет слишком большую емкость (capacity) и вы хотите сбросить выделенную память
- Когда вы работаете с пулом потоков и хотите избежать состояния гонки (race condition) при переиспользовании одного объекта
- Если вы точно знаете, что следующая строка будет значительно меньше предыдущей, и не хотите держать большой буфер
В таких случаях можно комбинировать подходы: сначала попробовать setLength(0), а если емкость превышает разумный порог (например, в 2 раза больше необходимой), то создать новый объект.
StringBuilder sb = new StringBuilder("Длинная строка...");
// ... много операций ...
int currentCapacity = sb.capacity();
int neededCapacity = 64; // Ожидаемый размер
if (currentCapacity > neededCapacity * 2) {
sb = new StringBuilder(neededCapacity);
} else {
sb.setLength(0);
}
Но в 95% случаев setLength(0) — это то, что вам нужно. Не усложняйте код без необходимости!