StringBuilder против конкатенации строк в toString() в Java
Учитывая 2 toString()
реализации ниже, какой из них является предпочтительным:
public String toString(){
return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}
или же
public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
?
Что еще более важно, учитывая, что у нас есть только 3 свойства, это может не иметь значения, но в какой момент вы бы переключились с +
согласен с StringBuilder
?
21 ответ
Версия 1 предпочтительнее, потому что она короче, и компилятор фактически превратит ее в версию 2 - без разницы в производительности.
Что еще более важно, учитывая, что у нас есть только 3 свойства, это может не иметь значения, но в какой момент вы переключаетесь с concat на builder?
В тот момент, когда вы объединяете в цикле - обычно, когда компилятор не может заменить StringBuilder
само собой.
Ключ в том, пишете ли вы одну конкатенацию в одном месте или накапливаете ее с течением времени.
В приведенном вами примере нет смысла явно использовать StringBuilder. (Посмотрите на скомпилированный код для вашего первого случая.)
Но если вы строите строку, например, внутри цикла, используйте StringBuilder.
Чтобы уточнить, если предположить, что огромный массив содержит тысячи строк, напишите следующий код:
...
String result = "";
for (String s : hugeArray) {
result = result + s;
}
очень трата времени и памяти по сравнению с:
...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
sb.append(s);
}
String result = sb.toString();
В большинстве случаев вы не увидите реальной разницы между этими двумя подходами, но наихудший сценарий легко построить, как этот:
public class Main
{
public static void main(String[] args)
{
long now = System.currentTimeMillis();
slow();
System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");
now = System.currentTimeMillis();
fast();
System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
}
private static void fast()
{
StringBuilder s = new StringBuilder();
for(int i=0;i<100000;i++)
s.append("*");
}
private static void slow()
{
String s = "";
for(int i=0;i<100000;i++)
s+="*";
}
}
Выход:
slow elapsed 11741 ms
fast elapsed 7 ms
Проблема в том, что добавление += к строке восстанавливает новую строку, поэтому она стоит что-то линейное по отношению к длине ваших строк (сумма обоих).
Итак - на ваш вопрос:
Второй подход был бы быстрее, но он менее читабелен и сложнее в обслуживании. Как я уже сказал, в вашем конкретном случае вы, вероятно, не увидите разницу.
Я предпочитаю:
String.format( "{a: %s, b: %s, c: %s}", a, b, c );
... потому что он короткий и читаемый.
Я бы не стал оптимизировать это для скорости, если бы вы не использовали его внутри цикла с очень большим количеством повторений и не измерили разницу в производительности.
Я согласен, что если вам нужно вывести много параметров, эта форма может запутаться (как сказано в одном из комментариев). В этом случае я бы переключился на более читаемую форму (возможно, используя ToStringBuilder из apache-commons - взятый из ответа matt b) и снова проигнорировал бы производительность.
Начиная с Java 1.5, простая однострочная конкатенация с "+" и StringBuilder.append() генерируют точно такой же байт-код.
Так что для удобства чтения кода используйте "+".
2 исключения:
- многопоточная среда: StringBuffer
- конкатенация в циклах: StringBuilder/StringBuffer
Я также столкнулся с моим боссом по поводу того, использовать ли append или +. Поскольку они используют Append(я до сих пор не могу понять, как они говорят каждый раз, когда создается новый объект). Поэтому я подумал сделать несколько R&D. Хотя мне нравится объяснение Майкла Боргвардта, но я просто хотел показать объяснение, если кому-то действительно понадобится знать в будущем.
/**
*
* @author Perilbrain
*/
public class Appc {
public Appc() {
String x = "no name";
x += "I have Added a name" + "We May need few more names" + Appc.this;
x.concat(x);
// x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
//System.out.println(x);
}
public void Sb() {
StringBuilder sbb = new StringBuilder("no name");
sbb.append("I have Added a name");
sbb.append("We May need few more names");
sbb.append(Appc.this);
sbb.append(sbb.toString());
// System.out.println(sbb.toString());
}
}
и разборка вышеупомянутого класса выходит как
.method public <init>()V //public Appc()
.limit stack 2
.limit locals 2
met001_begin: ; DATA XREF: met001_slot000i
.line 12
aload_0 ; met001_slot000
invokespecial java/lang/Object.<init>()V
.line 13
ldc "no name"
astore_1 ; met001_slot001
.line 14
met001_7: ; DATA XREF: met001_slot001i
new java/lang/StringBuilder //1st object of SB
dup
invokespecial java/lang/StringBuilder.<init>()V
aload_1 ; met001_slot001
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
ldc "I have Added a nameWe May need few more names"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
aload_0 ; met001_slot000
invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
astore_1 ; met001_slot001
.line 15
aload_1 ; met001_slot001
aload_1 ; met001_slot001
invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
pop
.line 18
return //no more SB created
met001_end: ; DATA XREF: met001_slot000i ...
; ===========================================================================
;met001_slot000 ; DATA XREF: <init>r ...
.var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001 ; DATA XREF: <init>+6w ...
.var 1 is x Ljava/lang/String; from met001_7 to met001_end
.end method
;44-1=44
; ---------------------------------------------------------------------------
; Segment type: Pure code
.method public Sb()V //public void Sb
.limit stack 3
.limit locals 2
met002_begin: ; DATA XREF: met002_slot000i
.line 21
new java/lang/StringBuilder
dup
ldc "no name"
invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
astore_1 ; met002_slot001
.line 22
met002_10: ; DATA XREF: met002_slot001i
aload_1 ; met002_slot001
ldc "I have Added a name"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 23
aload_1 ; met002_slot001
ldc "We May need few more names"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 24
aload_1 ; met002_slot001
aload_0 ; met002_slot000
invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
pop
.line 25
aload_1 ; met002_slot001
aload_1 ; met002_slot001
invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 28
return
met002_end: ; DATA XREF: met002_slot000i ...
;met002_slot000 ; DATA XREF: Sb+25r
.var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001 ; DATA XREF: Sb+9w ...
.var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
.end method
;96-49=48
; ---------------------------------------------------------------------------
Из приведенных выше двух кодов видно, что Майкл прав. В каждом случае создается только один объект SB.
Смотрите пример ниже:
//java8
static void main(String[] args) {
case1();//str.concat
case2();//+=
case3();//StringBuilder
}
static void case1() {
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
String str = "";
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str = str.concat(UUID.randomUUID()+"---");
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
}
static void case2() {
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
String str = "";
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str+=UUID.randomUUID()+"---";
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
}
static void case3() {
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
StringBuilder str = new StringBuilder("");
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str.append(UUID.randomUUID()+"---");
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
}
static void saveTime(List<Long> executionTimes, long startTime) {
executionTimes.add(System.currentTimeMillis()-startTime);
if(executionTimes.size()%CALC_AVG_EVERY == 0) {
out.println("average time for "+executionTimes.size()+" concatenations: "+
NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(()->0))+
" ms avg");
executionTimes.clear();
}
}
Выход:
среднее время для 10000 объединений: 0,096 мс
среднее время для 10000 конкатенаций: 0,185 мс
среднее время для 10000 конкатенаций: 0,327 мс
среднее время для 10000 объединений: 0,501 мс
среднее время для 10000 объединений: 0,656 мс
Созданная строка длины:1950000 в 17745 мс
среднее время для 10000 конкатенаций: 0,21 мс
среднее время для 10000 объединений: 0,652 мс
среднее время для 10000 конкатенаций: 1,129 мс
среднее время для 10000 конкатенаций: 1,727 мс
среднее время для 10000 объединений: 2,302 мс
Созданная строка длины:1950000 за 60279 мс
среднее время для 10000 объединений: 0,002 мс
среднее время для 10000 объединений: 0,002 мс
среднее время для 10000 объединений: 0,002 мс
среднее время для 10000 объединений: 0,002 мс
среднее время для 10000 объединений: 0,002 мс
Созданная строка длиной:1950000 в 100 мс
По мере увеличения длины строки время конкатенации увеличивается.
Вот где StringBuilder
обязательно нужно.
Как видите, конкатенация: UUID.randomUUID()+"---"
, не очень влияет на время.
PS: я не думаю, что когда использовать StringBuilder в Java, это действительно дубликат этого.
Этот вопрос говорит о toString()
который в большинстве случаев не выполняет конкатенации огромных строк.
Используя последнюю версию Java(1.8) разборка (javap -c
) показывает оптимизацию, представленную компилятором. +
также sb.append()
сгенерирует очень похожий код. Тем не менее, стоит проверить поведение, если мы используем +
в цикле.
Добавление строк с помощью + в цикле for
Джава:
public String myCatPlus(String[] vals) {
String result = "";
for (String val : vals) {
result = result + val;
}
return result;
}
ByteCode:(for
отрывок из цикла)
12: iload 5
14: iload 4
16: if_icmpge 51
19: aload_3
20: iload 5
22: aaload
23: astore 6
25: new #3 // class java/lang/StringBuilder
28: dup
29: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload 6
38: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc 5, 1
48: goto 12
Добавление строк с использованием stringbuilder.append
Джава:
public String myCatSb(String[] vals) {
StringBuilder sb = new StringBuilder();
for(String val : vals) {
sb.append(val);
}
return sb.toString();
}
ByteCdoe:(for
отрывок из цикла)
17: iload 5
19: iload 4
21: if_icmpge 43
24: aload_3
25: iload 5
27: aaload
28: astore 6
30: aload_2
31: aload 6
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc 5, 1
40: goto 17
43: aload_2
Есть небольшая явная разница. В первом случае, где +
был использован, новый StringBuilder
создается для каждой итерации цикла, а сгенерированный результат сохраняется toString()
звоните (с 29 по 41). Таким образом, вы генерируете промежуточные строки, которые вам действительно не нужны при использовании +
оператор в for
петля.
В Java 9 версия 1 должна быть быстрее, потому что она преобразуется в invokedynamic
вызов. Более подробную информацию можно найти в JEP-280:
Идея состоит в том, чтобы заменить весь танец добавления StringBuilder простым вызовом динамического вызова java.lang.invoke.StringConcatFactory, который будет принимать значения, требующие объединения.
По соображениям производительности, использование +=
(String
объединение) не рекомендуется. Причина почему: Java String
является неизменным, каждый раз, когда производится новая конкатенация String
создается (новый отпечаток отличается от старого, уже находящегося в пуле строк). Создание новых строк оказывает давление на ГХ и замедляет программу: создание объектов стоит дорого.
Приведенный ниже код должен сделать его более практичным и понятным одновременно.
public static void main(String[] args)
{
// warming up
for(int i = 0; i < 100; i++)
RandomStringUtils.randomAlphanumeric(1024);
final StringBuilder appender = new StringBuilder();
for(int i = 0; i < 100; i++)
appender.append(RandomStringUtils.randomAlphanumeric(i));
// testing
for(int i = 1; i <= 10000; i*=10)
test(i);
}
public static void test(final int howMany)
{
List<String> samples = new ArrayList<>(howMany);
for(int i = 0; i < howMany; i++)
samples.add(RandomStringUtils.randomAlphabetic(128));
final StringBuilder builder = new StringBuilder();
long start = System.nanoTime();
for(String sample: samples)
builder.append(sample);
builder.toString();
long elapsed = System.nanoTime() - start;
System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);
String accumulator = "";
start = System.nanoTime();
for(String sample: samples)
accumulator += sample;
elapsed = System.nanoTime() - start;
System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);
start = System.nanoTime();
String newOne = null;
for(String sample: samples)
newOne = new String(sample);
elapsed = System.nanoTime() - start;
System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}
Результаты пробега сообщаются ниже.
builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us
builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us
builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us
builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us
builder - 10000 - elapsed: 3364us
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us
Не учитывая результаты для 1 конкатенации (JIT еще не выполнил свою работу), даже для 10 конкатенаций снижение производительности является значимым; для тысяч конкатенаций разница огромна.
Уроки, извлеченные из этого очень быстрого эксперимента (легко воспроизводимого с помощью приведенного выше кода): никогда не используйте +=
объединять строки вместе, даже в самых простых случаях, когда требуется несколько объединений (как уже говорилось, создание новых строк в любом случае стоит дорого и оказывает давление на сборщик мусора).
Apache Commons-Lang имеет класс ToStringBuilder, который очень прост в использовании. Он отлично справляется как с логикой добавления, так и с форматированием того, как вы хотите, чтобы ваша строка была похожа.
public void toString() {
ToStringBuilder tsb = new ToStringBuilder(this);
tsb.append("a", a);
tsb.append("b", b)
return tsb.toString();
}
Вернет вывод, который выглядит как com.blah.YourClass@abc1321f[a=whatever, b=foo]
,
Или в более сжатой форме, используя цепочку:
public void toString() {
return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}
Или, если вы хотите использовать отражение, чтобы включить каждое поле класса:
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
Вы также можете настроить стиль ToString, если хотите.
Я думаю, что мы должны пойти с подходом добавления StringBuilder. Причина в
Конкатенация String каждый раз будет создавать новый строковый объект (поскольку String является неизменным объектом), поэтому он будет создавать 3 объекта.
С помощью String Builder будет создан только один объект [StringBuilder является изменяемым], и к нему добавляется следующая строка.
Сделайте метод toString настолько читаемым, насколько это возможно!
Единственное исключение для этого в моей книге, если вы можете доказать мне, что он потребляет значительные ресурсы:) (Да, это означает профилирование)
Также обратите внимание, что компилятор Java 5 генерирует более быстрый код, чем рукописный подход "StringBuffer", используемый в более ранних версиях Java. Если вы используете "+", это и будущие улучшения будут бесплатными.
Стоит отметить, что, как указал @ZhekaKozlov,
быстрее, чем Java 9, если только JVM не знает, как его оптимизировать (например, конкатенация в цикле).
Я проверил байт-код для следующего кода (в Java 17):
public class StringBM {
public String toStringPlus(String a) {
return "{a:" + a + ", b:" + ", c: " + "}";
}
public String toStringBuilder(String a) {
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:")
.append(", c:")
.append("}")
.toString();
}
}
За
toStringPlus
:
0: aload_1
1: invokedynamic #7, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
6: areturn
за
toStringBuilder
:
0: new #11 // class java/lang/StringBuilder
3: dup
4: bipush 100
6: invokespecial #13 // Method java/lang/StringBuilder."<init>":(I)V
9: astore_2
10: aload_2
11: ldc #16 // String {a:
13: invokevirtual #18 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: aload_1
17: invokevirtual #18 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: ldc #22 // String , b:
22: invokevirtual #18 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: ldc #24 // String , c:
27: invokevirtual #18 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: ldc #26 // String }
32: invokevirtual #18 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
38: areturn
Версия просто вызывает динамическую функцию
makeConcatWithConstants
и передать аргумент метода {a:\u0001, b:, c: } (
\u0001
является заполнителем параметра).
В то время как
StringBuilder
версия должна делать это «честным» способом.
Я думаю, мы можем понять, почему
+
теперь быстрее.
Вот что я проверил на Java8
- Использование конкатенации строк
Использование StringBuilder
long time1 = System.currentTimeMillis(); usingStringConcatenation(100000); System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms"); time1 = System.currentTimeMillis(); usingStringBuilder(100000); System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms"); private static void usingStringBuilder(int n) { StringBuilder str = new StringBuilder(); for(int i=0;i<n;i++) str.append("myBigString"); } private static void usingStringConcatenation(int n) { String str = ""; for(int i=0;i<n;i++) str+="myBigString"; }
Это настоящий кошмар, если вы используете конкатенацию строк для большого количества строк.
usingStringConcatenation 29321 ms
usingStringBuilder 2 ms
Похоже, есть некоторые споры о том, нужно ли использовать StringBuilder с текущими компиляторами. Так что я думал, что я дам свои 2 цента опыта.
у меня есть JDBC
результирующий набор из 10 тыс. записей (да, мне нужно, чтобы все они были в одной партии). Использование оператора + занимает около 5 минут на моей машине с Java 1.8
, С помощью stringBuilder.append("")
занимает меньше секунды для того же запроса.
Так что разница огромна. Внутри петли StringBuilder
намного быстрее
С точки зрения производительности конкатенация строк с использованием "+" обходится дороже, потому что она должна создавать совершенно новую копию строки, поскольку строки неизменяемы в Java. Это играет особую роль, если конкатенация очень частая, например: внутри цикла. Вот что предлагает моя ИДЕЯ, когда я пытаюсь сделать такую вещь:
Основные правила:
- Внутри одного строкового присваивания можно использовать конкатенацию строк.
- Если вы зацикливаетесь на создании большого блока символьных данных, выберите StringBuffer.
- Использование += для String всегда будет менее эффективным, чем использование StringBuffer, поэтому он должен вызывать предупреждающие сигналы - но в некоторых случаях полученная оптимизация будет незначительной по сравнению с проблемами читабельности, поэтому используйте свой здравый смысл.
Вот хороший блог Джона Скита на эту тему.
Могу ли я указать, что если вы собираетесь перебирать коллекцию и использовать StringBuilder, вы можете проверить Apache Commons Lang и StringUtils.join () (в разных вариантах)?
Независимо от производительности, это избавит вас от необходимости создавать StringBuilders и циклы для того, что кажется в миллионный раз.
Я сравнил четыре разных подхода для сравнения производительности. Я точно не знаю, что происходит с gc, но для меня важно время. Важным фактором здесь является компилятор. Я использовал jdk1.8.0_45 под платформой window8.1.
concatWithPlusOperator = 8
concatWithBuilder = 130
concatWithConcat = 127
concatStringFormat = 3737
concatWithBuilder2 = 46
public class StringConcatenationBenchmark {
private static final int MAX_LOOP_COUNT = 1000000;
public static void main(String[] args) {
int loopCount = 0;
long t1 = System.currentTimeMillis();
while (loopCount < MAX_LOOP_COUNT) {
concatWithPlusOperator();
loopCount++;
}
long t2 = System.currentTimeMillis();
System.out.println("concatWithPlusOperator = " + (t2 - t1));
long t3 = System.currentTimeMillis();
loopCount = 0;
while (loopCount < MAX_LOOP_COUNT) {
concatWithBuilder();
loopCount++;
}
long t4 = System.currentTimeMillis();
System.out.println("concatWithBuilder = " + (t4 - t3));
long t5 = System.currentTimeMillis();
loopCount = 0;
while (loopCount < MAX_LOOP_COUNT) {
concatWithConcat();
loopCount++;
}
long t6 = System.currentTimeMillis();
System.out.println("concatWithConcat = " + (t6 - t5));
long t7 = System.currentTimeMillis();
loopCount = 0;
while (loopCount < MAX_LOOP_COUNT) {
concatStringFormat();
loopCount++;
}
long t8 = System.currentTimeMillis();
System.out.println("concatStringFormat = " + (t8 - t7));
long t9 = System.currentTimeMillis();
loopCount = 0;
while (loopCount < MAX_LOOP_COUNT) {
concatWithBuilder2();
loopCount++;
}
long t10 = System.currentTimeMillis();
System.out.println("concatWithBuilder2 = " + (t10 - t9));
}
private static void concatStringFormat() {
String s = String.format("%s %s %s %s ", "String", "String", "String", "String");
}
private static void concatWithConcat() {
String s = "String".concat("String").concat("String").concat("String");
}
private static void concatWithBuilder() {
StringBuilder builder=new StringBuilder("String");
builder.append("String").append("String").append("String");
String s = builder.toString();
}
private static void concatWithBuilder2() {
String s = new StringBuilder("String").append("String").append("String").append("String").toString();
}
private static void concatWithPlusOperator() {
String s = "String" + "String" + "String" + "String";
}
}
Для таких простых строк я предпочитаю использовать
"string".concat("string").concat("string");
По порядку, я бы сказал, что предпочтительным методом построения строки является использование StringBuilder, String#concat(), а затем перегруженный оператор +. StringBuilder - это значительное увеличение производительности, когда работа с большими строками так же, как использование оператора + - это значительное снижение производительности (экспоненциально большое уменьшение при увеличении размера строки). Единственная проблема с использованием.concat() заключается в том, что он может генерировать исключения NullPointerException.