Почему StringBuilder не создает лишних объектов String в куче при конкатенации?

Ответ

StringBuilder минимизирует создание объектов благодаря изменяемому внутреннему буферу (массиву char[]). В отличие от операции конкатенации строк (+), которая каждый раз создает новый неизменяемый объект String, StringBuilder модифицирует содержимое своего буфера.

Ключевой механизм:

  1. При создании StringBuilder инициализируется с буфером определенной емкости.
  2. Метод append() добавляет данные в этот буфер. Если места не хватает, буфер расширяется (обычно по формуле новая_емкость = (старая_емкость * 2) + 2), что все равно происходит гораздо реже, чем создание нового String при каждой конкатенации.
  3. Финальная строка создается только при вызове toString().

Сравнение на примере:

Неэффективно с + (в цикле):

String result = "";
for (int i = 0; i < 100; i++) {
    result += i; // На КАЖДОЙ итерации создается новый String!
}

Эффективно с StringBuilder (в цикле):

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i); // Данные добавляются в существующий буфер
}
String result = sb.toString(); // Создается только один итоговый String

Вывод: StringBuilder следует использовать для многократной конкатенации строк, особенно в циклах, чтобы избежать накладных расходов на создание и сборку мусора от промежуточных объектов String.

Ответ 18+ 🔞

А, ну ты смотри, какой умный вопрос подкинули! Решили разобраться, чем этот ваш StringBuilder так крут, да? Ёпта, сейчас объясню на пальцах, без всей этой академической пиздобратии.

Представь себе, что строка в Java — это, блядь, священная корова. Раз родилась — изменить её нихуя нельзя. Хочешь к ней что-то добавить? Так, сука, придётся зарезать старую корову, из её мяса и нового куска слепить новую тушу, а старую — на помойку истории. И так каждый раз, когда ты пишешь этот плюсик +. Представляешь, какой бойнище за окном, если в цикле на 100 раз? Заебёшься трупы выносить, сборщик мусора с ума сойдёт!

А теперь смотри сюда, хитрая жопа StringBuilder — это как умный мясник с большой-пребольшой морозилкой (этот самый буфер char[]). Ты ему говоришь: «Вася, мне надо колбасу собрать из 100 кусочков». Он не бегает каждый раз на склад за новой коробкой. Нет! Он кладёт первый кусок в морозилку, второй, третий... Если места вдруг не хватает, он не паникует, а просто, блядь, берёт морозилку в два раза больше (старая_емкость * 2) + 2) и перекладывает туда всё накопленное добро. И только в самом конце, когда всё готово, он достаёт эту гигантскую сосиску и говорит: «Вот, держи, готовый продукт!» (это вызов toString()).

Давай глянем на живых примерах, а то я, блядь, совсем заболтался.

Тупой способ (как разводить коровье кладбище):

String result = "";
for (int i = 0; i < 100; i++) {
    result += i; // На КАЖДОМ, блядь, витке цикла режется новая корова! Пиздец!
}

Охуенный способ (с мясником и морозилкой):

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i); // Просто кидаем куски в морозилку. Никакой резни!
}
String result = sb.toString(); // И только один раз достаём готовое!

Короче, вывод простой, как три копейки: если тебе надо что-то собирать из кусочков, особенно в цикле — не будь мудаком, не плоди мусор. Бери StringBuilder и спи спокойно. А то твоя программа будет не работать, а заниматься ебанистикой с постоянным созданием и уничтожением объектов. В рот меня чих-пых, зачем такие сложности?