← Назад к вопросам

Как работает под капотом StringBuilder?

1.3 Junior🔥 281 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает 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());
    }
}

Итоги

  1. StringBuilder использует динамический массив char[]
  2. Ёмкость растёт экспоненциально (×2 + 2) при необходимости
  3. append() добавляет символы в O(1) амортизированной сложности
  4. toString() создаёт новый String
  5. StringBuffer — синхронизированная альтернатива