Как работает под капотом StringBuilder?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает StringBuilder под капотом
StringBuilder — это мутируемый класс для эффективного создания строк в Java. В отличие от String, которая неизменяема, StringBuilder внутренне модифицирует исходный массив символов.
Внутренняя структура
StringBuilder использует динамический массив символов (char[]):
public final class StringBuilder extends AbstractStringBuilder {
char[] value; // Основной буфер символов
int count; // Текущая длина содержимого
}
При создании StringBuilder резервируется буфер с начальной ёмкостью (capacity):
// Дефолтная ёмкость 16 символов
StringBuilder sb = new StringBuilder(); // capacity = 16
// С явным размером
StringBuilder sb = new StringBuilder(100); // capacity = 100
// Из существующей строки (ёмкость = длина + 16)
StringBuilder sb = new StringBuilder("Hello"); // capacity = 21
Операция append()
Когда вы вызываете append(), StringBuilder добавляет символы в массив:
public StringBuilder append(String str) {
if (str == null) {
appendNull();
} else {
int len = str.length();
ensureCapacity(count + len); // Проверка ёмкости
str.getChars(0, len, value, count);
count += len;
}
return this;
}
Динамическое расширение буфера
Когда текущее содержимое превышает ёмкость, происходит автоматическое расширение:
private void ensureCapacity(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
expandCapacity(minimumCapacity);
}
}
void expandCapacity(int minimumCapacity) {
// Новая ёмкость = (старая * 2) + 2
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0) {
newCapacity = minimumCapacity;
}
// Копируем старый буфер в новый
char[] newValue = new char[newCapacity];
System.arraycopy(value, 0, newValue, 0, count);
value = newValue;
}
Ключевой момент: каждый раз при расширении происходит копирование всего содержимого в новый массив. Это операция O(n), но она случается логарифмическое количество раз благодаря экспоненциальному росту.
Преимущество перед String
Без StringBuilder:
String result = "";
for (int i = 0; i < 1000; i++) {
result += "data";
}
С StringBuilder:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("data");
}
String result = sb.toString();
Метод toString()
public String toString() {
return new String(value, 0, count);
}
StringBuffer vs StringBuilder
StringBuffer — синхронизированная версия:
public synchronized StringBuffer append(String str) {
// потокобезопасен
}
public StringBuilder append(String str) {
// НЕ потокобезопасен, быстрее
}
Практический пример
StringBuilder sb = new StringBuilder();
System.out.println("Ёмкость: " + sb.capacity()); // 16
for (int i = 0; i < 30; i++) {
sb.append("x");
if (i == 15 || i == 17 || i == 29) {
System.out.println("После " + (i+1) + " символов, ёмкость: " + sb.capacity());
}
}
Итоги
- StringBuilder использует динамический массив char[]
- Ёмкость растёт экспоненциально (×2 + 2) при необходимости
- append() добавляет символы в O(1) амортизированной сложности
- toString() создаёт новый String
- StringBuffer — синхронизированная альтернатива