Конкатенация строк в Java
Объединяйте строки в Java с помощью оператора +, concat() и StringBuilder, и сравнивайте их производительность.
«Конкатенация» означает объединение строк одна за другой. Java предоставляет несколько способов это сделать — +, concat, String.format, текстовые блоки, StringBuilder, String.join. Они различаются по читаемости и, при использовании в циклах, по производительности.
Оператор +
+ — самый распространённый способ:
String first = "Ada";
String last = "Lovelace";
String full = first + " " + last; // "Ada Lovelace"Он также преобразует нестроковые типы в строки через их toString():
int age = 36;
String s = "Age: " + age; // "Age: 36"За кулисами компилятор превращает a + b + c в единственный вызов StringBuilder.append(...).append(...).toString(). Поэтому одно цепочечное выражение с + работает эффективно.
concat
String.concat(other) возвращает текущую строку, за которой следует other:
"Hello, ".concat("World!"); // "Hello, World!"Это фактически то же самое, что +, но принимает только String (без автоконвертации). В большинстве кода просто используется +.
String.format и formatted
Для конкатенации нескольких частей с форматированием, специфичным для типов, String.format выглядит чище, чем длинные цепочки +:
String name = "Ada";
int age = 36;
String s = String.format("%s is %d years old", name, age);Начиная с Java 15, метод экземпляра formatted является эквивалентом и читается слева направо:
"%s is %d years old".formatted(name, age);Текстовые блоки для многострочных строк
Для многострочных строк синтаксис текстовых блоков (Java 15+) позволяет избежать длинных цепочек с \n:
String json = """
{
"name": "%s",
"age": %d
}
""".formatted(name, age);StringBuilder — для циклов
Строки Java неизменяемы — после создания String никогда не меняется. Поэтому каждое использование + со String создаёт совершенно новый объект. В цикле с тысячами итераций это означает большое количество короткоживущих объектов и копирований. Вместо этого используйте StringBuilder — у него есть единственный изменяемый внутренний буфер, который растёт на месте:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("line ").append(i).append("\n");
}
String result = sb.toString();Методы StringBuilder, которые вам понадобятся:
append(any)— добавить в конец (возвращает строитель, чтобы можно было выстраивать цепочки).insert(index, any)— вставить на определённую позицию.delete(start, end)— удалить диапазон.reverse()— перевернуть содержимое строителя.length()иsetLength(n)— прочитать / усечь.toString()— получить итоговуюString.
Существует также StringBuffer, который идентичен, но потокобезопасен (каждый метод является synchronized). Он почти никогда не нужен — используйте StringBuilder, если только вы не разделяете строитель между потоками намеренно.
String.join — для коллекций
Когда у вас есть коллекция строк и разделитель, String.join — самый чистый вариант:
String csv = String.join(",", "a", "b", "c"); // "a,b,c"
String csv2 = String.join(", ", List.of("a", "b", "c")); // "a, b, c"Для более сложного объединения (коллекторы, условное включение) смотрите Collectors.joining из потоков далее в книге.
Когда что использовать
Примерное дерево решений:
- Одна или две части, читаются слева направо →
+. - Несколько частей с форматированием типов →
String.formatилиformatted. - Многострочный литерал → текстовый блок, опционально с
.formatted. - Построение по частям в цикле →
StringBuilder. - Объединение коллекции с разделителем →
String.join.
Для небольших конкатенаций современные компиляторы и JVM (с конкатенацией строк на основе invokedynamic, введённой в Java 9) делают + очень быстрым — производительность не является причиной избегать его в простых случаях.
Демонстрация случая с циклом
Что дальше
Специальные символы и управляющие последовательности Java — что делают \n, \t, \" и юникодные управляющие последовательности.