Эффективна ли конкатенация строк при назначении? - PullRequest
0 голосов
/ 11 февраля 2019

Я знаю, что использование оператора конкатенации "+" для построения строк очень неэффективно, и поэтому рекомендуется использовать класс StringBuilder, но мне было интересно, если этот тип шаблона также неэффективен?

String some = a + "\t" + b + "\t" + c + "\t" + d + "\t" + e;

Полагаю, здесь компилятор прекрасно оптимизирует назначение или нет?

Ответы [ 3 ]

0 голосов
/ 11 февраля 2019

В общем случае конкатенация строк с + и использованием StringBuilder абсолютно правильная и работает.Но в разных ситуациях конкатенация с + становится менее эффективной, чем с использованием StringBuilder.

Конкатенация строк NOT IN LOOP - EFFICIENT !!!

Это дает хорошую производительность, потому что JVM преобразует этоиспользуя StringBuilder.

String some = a + "\t" + b + "\t" + c + "\t" + d + "\t" + e;

Это нормально, потому что JVM внутренне меняет этот код на следующий:

String some = new StringBuilder().append(a).append('\t').append(c).append('\t')
                                 .append(d).append('\t').append(e).toString();

PS StringBuilder имеет внутреннийбуфер char[].Если вы знаете, как долго будет строка результата, лучше зарезервировать весь буфер в начале.Например, в случае конечной строки будет не более 1024 символов , тогда вы можете сделать new StringBuilder(1024)

Объединение строк в цикле - НЕ ЭФФЕКТИВНО !!!

Это делаетплохая производительность, потому что JVM не может обернуть цикл while одним StringBuilder, например так:

StringBuilder buf = new StringBuilder();

for (int i = 0; i < 10; i++)
    buf.append(a).append('\t').append(c).append('\t')
       .append(d).append('\t').append(e).append('t');

String some = buf.toString();

, но JVM все еще может оптимизировать все объединения в каждой итерации цикла;например:

String some = "";

for (int i = 0; i < 10; i++) {
    some = new StringBuilder(some).append(a).append('\t').append(c).append('\t')
                               .append(d).append('\t').append(e).append('t');
}

Как видите, есть некоторые недостатки использования конкатенации строк в цикле.

0 голосов
/ 12 февраля 2019

Ваша предпосылка «что использование оператора конкатенации« + »для построения строк очень неэффективна», неверна.Во-первых, сама конкатенация строк не является дешевой операцией, так как она подразумевает создание новой строки, содержащей все конкатенированные строки, следовательно, необходимо копировать содержимое символа.Но это всегда применимо, независимо от того, как вы это делаете.

Когда вы используете оператор +, вы говорите, что хотите сделать, не говоря, как это сделать.,Даже спецификация языка Java не требует конкретной стратегии реализации, за исключением того, что константа времени компиляции должна выполняться во время компиляции.Таким образом, для констант времени компиляции оператор + является наиболее эффективным решением¹.

На практике все обычно используемые компиляторы из Java 5 в Java 8 генерируют код с использованием StringBuilderпод капотом (до Java 5 они использовали StringBuffer).Это относится к таким утверждениям, как ваше, поэтому замена его на использование StringBuilder вручную не принесет особого успеха.Вы могли бы быть немного лучше, чем типичный код, сгенерированный компилятором, предоставляя разумную начальную емкость, но это все.

Начиная с Java 9, компиляторы генерируют инструкцию invokedynamic, которая позволяет среде выполнения предоставлять фактический код, выполняющийконкатенация.Это может быть код StringBuilder, похожий на тот, который использовался в прошлом, но также и нечто совершенно иное.В частности, предоставляемый во время выполнения код может получить доступ к специфическим функциям реализации, чего не может сделать код приложения.Поэтому теперь конкатенация строк с помощью + может быть даже быстрее, чем код на основе StringBuilder.

Поскольку это применимо только к одному выражению конкатенации, при выполнении строкового построения с использованием нескольких операторов или даже цикла,последовательное использование StringBuilder в течение всей конструкции может быть быстрее, чем операции множественной конкатенации.Однако, поскольку код выполняется в оптимизирующей среде, когда JVM распознает некоторые из этих шаблонов, даже об этом нельзя сказать наверняка.

Это время вспомнить старое правило, чтобы попытаться только оптимизироватьпроизводительность, когда есть реальная проблема с производительностью.И всегда проверяйте с помощью объективных измерительных инструментов, действительно ли попытка оптимизации повышает производительность.Существует множество распространенных мифов, неправильных или устаревших, о приемах оптимизации производительности.

¹, за исключением случаев, когда вы повторяете детали и хотите уменьшить размер файла класса

0 голосов
/ 11 февраля 2019

Этот конкретный пример будет встроен компилятором:

String a = "a";
String b = "bb";
String c = "ccc";
String some = a + "\t" + b + "\t" + c;

Java 9+ встроит это, используя invokedynamic с makeConcatWithConstants , что делает его эффективным.В соответствии с выводом javap -v:

Code:
  stack=3, locals=5, args_size=1
     0: ldc           #2                  // String a
     2: astore_1
     3: ldc           #3                  // String bb
     5: astore_2
     6: ldc           #4                  // String ccc
     8: astore_3
     9: aload_1
    10: aload_2
    11: aload_3
    12: invokedynamic #5,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    17: astore        4
    19: return

Однако, если a b и c являются компиляторами времени компиляции, он дополнительно оптимизирует код:

final String a = "a";
final String b = "bb";
final String c = "ccc";
String some = a + "\t" + b + "\t" + c;

иsome будет загружено с постоянным значением:

Code:
  stack=1, locals=5, args_size=1
     0: ldc           #2                  // String a
     2: astore_1
     3: ldc           #3                  // String bb
     5: astore_2
     6: ldc           #4                  // String ccc
     8: astore_3
     9: ldc           #5                  // String a\tbb\tccc
    11: astore        4
    13: return

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

...