В чем разница между конкатенацией и методом append в StringBuilder?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между конкатенацией и StringBuilder.append()
Конкатенация со строками через +
Когда вы используете оператор +, Java компилирует это в вызовы StringBuilder под капотом, но неэффективно в циклах:
// Конкатенация
String str = "";
for (int i = 0; i < 1000; i++) {
str = str + "hello"; // создаёт НОВЫЙ объект на каждой итерации!
}
Это эквивалентно:
String str = "";
for (int i = 0; i < 1000; i++) {
str = new StringBuilder(str)
.append("hello")
.toString(); // преобразование в String
}
Каждая итерация создаёт новый StringBuilder, добавляет текст и преобразует обратно в String. Это очень неэффективно!
StringBuilder.append()
StringBuilder - это мутируемый класс, который растёт по мере необходимости:
// StringBuilder - правильный подход
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("hello"); // добавляет в существующий объект
}
String result = sb.toString(); // преобразование один раз в конце
StringBuilder использует внутренний массив символов, который расширяется при необходимости. Это намного более эффективно.
Производительность
Разница в производительности огромна:
public class StringPerformanceTest {
public static void main(String[] args) {
// Конкатенация - медленно
long start = System.currentTimeMillis();
String result1 = "";
for (int i = 0; i < 10000; i++) {
result1 = result1 + "text"; // O(n²) временная сложность
}
long concatTime = System.currentTimeMillis() - start;
System.out.println("Конкатенация: " + concatTime + "ms"); // ~1000ms+
// StringBuilder - быстро
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("text"); // O(n) временная сложность
}
String result2 = sb.toString();
long sbTime = System.currentTimeMillis() - start;
System.out.println("StringBuilder: " + sbTime + "ms"); // ~1ms
// Результат: конкатенация медленнее в 1000+ раз!
}
}
Внутренняя реализация StringBuilder
StringBuilder содержит массив символов и индекс текущей позиции:
public class StringBuilder extends AbstractStringBuilder {
char[] value; // внутренний массив
int count; // текущая длина
public StringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count); // копирует в массив
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
// расширяем массив
char[] newValue = new char[newCapacity];
System.arraycopy(value, 0, newValue, 0, count);
value = newValue;
}
}
}
Массив растёт по мере необходимости, обычно в 2 раза, что амортизирует стоимость копирования.
Одиночная конкатенация
Для одиночной конкатенации разницы практически нет:
// Так OK - компилятор оптимизирует
String greeting = "Hello " + "World"; // компилируется в "Hello World"
// Так тоже OK для отдельных операций
String full = firstName + " " + lastName; // приемлемо
// Но в цикле - ПЛОХО
for (String s : collection) {
result = result + s; // никогда так не делай!
}
StringBuffer vs StringBuilder
StringBuffer - это синхронизированная версия StringBuilder:
// StringBuffer - потокобезопасен (синхронизирован)
public synchronized StringBuffer append(String str) {
// ...
}
// StringBuilder - НЕ потокобезопасен (быстрее)
public StringBuilder append(String str) {
// ...
}
Используй StringBuilder если не требуется многопоточность (обычно это так).
Лучшие практики
// ❌ ПЛОХО - конкатенация в цикле
String result = "";
for (User user : users) {
result = result + user.getName() + ",";
}
// ✅ ХОРОШО - StringBuilder
StringBuilder sb = new StringBuilder();
for (User user : users) {
sb.append(user.getName()).append(",");
}
String result = sb.toString();
// ✅ ХОРОШО - методы join() при наличии
String result = String.join(",",
users.stream().map(User::getName).collect(Collectors.toList()));
// ✅ ХОРОШО - Streams
String result = users.stream()
.map(User::getName)
.collect(Collectors.joining(","));
Вывод
Конкатенация через + создаёт новый StringBuilder на каждой итерации, что приводит к O(n²) временной сложности. StringBuilder.append() создаёт один объект с растущим массивом, что даёт O(n) сложность. Всегда используй StringBuilder для построения строк в циклах и при множественных операциях.