Объединение строк Java в вызове stringbuilder

#java #string #stringbuilder

#java #строка #stringbuilder

Вопрос:

Насколько я знаю, StringBuilder помогает уменьшить использование памяти, не создавая временные экземпляры строк в пуле строк во время конкатов. Но что произойдет, если я сделаю что-то подобное:

 StringBuilder sb = new StringBuilder("bu");
sb.append("b" "u");
  

Компилируется ли он в

 sb.append("b");
sb.append("u");
  

? Или это зависит от флагов оптимизации? Или я теряю всю выгоду, если stringbuilders?
Или эта очередь не имеет смысла? 🙂

Ответ №1:

Он компилируется в sb.append("bu") , потому что компилятор преобразует конкатенацию нескольких строковых литералов в один строковый литерал.

Если бы у вас было

 String a = "a";
sb.append(a   "b");
  

это скомпилировало бы его в

 String a = "a";
String temp = a   "b"; // useless creation of a string here
sb.append(temp);
  

Поэтому вы должны предпочесть

 sb.append(a);
sb.append("b");
  

в этом случае.

Комментарии:

1. Является ли это чем-то относительно новым для Java? Я всегда считал, что конкатенация строк приведет к появлению нескольких строк, поскольку они неизменяемы? Или это применимо только при конкатенации строковых переменных?

2. Нет, это не ново. Очень полезно иметь возможность правильно форматировать (в нескольких строках) длинный строковый литерал, например, длинный запрос SQL или JPQL.

3. для 2-го примера кода будет создана не только строка, но и дополнительный StringBuilder.

4. @JBNizet: Учитывая String cd = "cd"; , как оптимизируется следующая строка? String abcdef = "a" "b" cd "e" "f"; ?? Спасибо!

Ответ №2:

Поскольку "b" "u" это выражение, которое вычисляется во время компиляции, оно будет скомпилировано так же, как если бы у вас было "bu" .

  0: new #2; //class StringBuilder
 3: dup
 4: ldc #3; //String bu
 6: invokespecial   #4; //Method StringBuilder."<init>":(String;)V
 9: astore_1
10: aload_1
11: ldc #3; //String bu
13: invokevirtual   #5; // StringBuilder.append:(String;)LStringBuilder;
  

Если бы у вас, с другой стороны, было две строковые переменные, эта оптимизация не сработала бы:

Следующий фрагмент…

 StringBuilder sb = new StringBuilder("bu");
String b = "b", u = "u";

sb.append(b   u);
  

…компилируется как:

 0:  new #2; //class StringBuilder
3:  dup
4:  ldc #3; //String bu
6:  invokespecial   #4; //Method StringBuilder."<init>":(String;)V
9:  astore_1
10: ldc #5; //String b
12: astore_2
13: ldc #6; //String u
15: astore_3
16: aload_1
17: new #2; //class StringBuilder
20: dup
21: invokespecial   #7; //Method StringBuilder."<init>":()V
24: aload_2
25: invokevirtual   #8; //Method StringBuilder.append:(String;)StringBuilder;
28: aload_3
29: invokevirtual   #8; //Method StringBuilder.append:(String;)StringBuilder;
32: invokevirtual   #9; //Method StringBuilder.toString:()String;
35: invokevirtual   #8; //Method StringBuilder.append:(String;)StringBuilder;
  

Т.е. что-то похожее на

 StringBuilder sb = new StringBuilder("bu");
String b = "b", u = "u";

StringBuilder temp = new StringBuilder();
temp.append(b);
temp.append(b);
String result = temp.toString();

sb.append(result);
  

Как вы можете видеть в строке 17-21 StringBuilder , для объединения a и создается дополнительный b . Результат String этого временного StringBuilder затем извлекается в строке 32 и добавляется к оригиналу StringBuilder в строке 35.


(Байт-код был сгенерирован javap командой, которая является частью JDK. Попробуйте, это действительно просто!)

Комментарии:

1. Вы догадались, что я действительно хотел спросить 🙂 Таким образом, оптимизация происходит, когда у меня есть только литералы, в случае переменных всегда используйте append .

2. @aioobe: Учитывая String cd = "cd"; , являются ли следующие два эквивалентными? String abcdef = "a" "b" cd "e" "f"; <==> Строка abcdef = «ab» cd «ef»;`? Спасибо!

3. @Gevorg, я так считаю, почему бы тебе не попробовать это с javap ? 🙂

4. Хахах, это не так. Просто сделайте javac Test.java , а затем javap -c Test .

5. @aioobe: Я сделал это, это было великолепно! 🙂 Он компилируется в "ab" cd "e" "f"; I tried с разными входными данными, в основном только литералы перед оптимизацией переменной. Очень интересно.. Я думаю, это может быть связано с естественным порядком ‘ ‘..

Ответ №3:

Нет, "b" "u" будет создана неизменяемая b строка, неизменяемая u строка, а затем будет создана третья неизменяемая bu строка, которая передается в StringBuilder экземпляр.

Комментарии:

1. Нет, этого не будет. Смотрите ответ @aioobe.

2. Я не могу поверить, что компилятор недостаточно умен, чтобы не создавать однобуквенные литералы, или, без такого большого отрицания, я считаю, что компилятор достаточно умен, чтобы создавать только bu строку.

3. @CarlosHeuberger — это не «умность». Это просто вопрос реализации спецификации языка Java.

4. @Stephen — следует читать как: люди, которые разработали и внедрили компилятор, достаточно умны…

5. @CarlosHeuberger — чтение и реализация спецификации — это ваше определение smart? Справедливости ради, есть большая вероятность, что спецификация отразила первый компилятор, а не наоборот.

Ответ №4:

НЕПРАВИЛЬНО:

 StringBuilder sb = new StringBuilder();
sb.append("b" "u");
  

ПРАВИЛЬНО:

 StringBuilder sb = new StringBuilder();
sb.append("b").append("u");
  

ЛУЧШЕ ВСЕГО: // Поскольку вы уже знали, что там будет! 😉

 StringBuilder sb = new StringBuilder();
sb.append("bu");
  

:)

Редактировать

Я предполагаю, что мой ответ выше неверен, когда речь идет только о литералах…

:/

Комментарии:

1. На самом деле, «НЕПРАВИЛЬНАЯ» версия эквивалентна «ЛУЧШЕЙ». Смотрите ответ @aioobe.

2. @StephenC: Знаете ли вы, как / если / почему компилятор оптимизирует следующее: `String abcde = «a» «b» c «d» «e»;?

3. @Gevorg — я знаю, что это так, потому что JLS говорит, что это так, и если бы компилятор этого не сделал, он не был бы допустимым компилятором Java.