Конкатенация строк: concat () vs & ldquo; + & rdquo; оператор

Предполагая строки a и b:

a += b
a = a.concat(b)

Под капотом они одно и то же?

Здесь concat декомпилирован как ссылка. Я хотел бы также иметь возможность декомпилировать оператор +, чтобы увидеть, что это делает.

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}
464
задан 06.06.2017, 10:10

8 ответов

Нет, не совсем.

1130 Во-первых, есть небольшая разница в семантике. Если a равно null, то a.concat(b) выбрасывает NullPointerException, но a+=b будет обрабатывать исходное значение a, как если бы это было null. Кроме того, метод concat() принимает только значения String, в то время как оператор + автоматически преобразует аргумент в строку (используя метод toString() для объектов). Таким образом, метод concat() более строг в отношении того, что он принимает.

Чтобы заглянуть под капот, напишите простой класс с a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

Теперь разберите с javap -c (входит в Sun JDK). Вы должны увидеть список, включающий:

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

Итак, a += b является эквивалентом

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

Метод concat должен быть быстрее. Однако с большим количеством строк метод StringBuilder выигрывает, по крайней мере, с точки зрения производительности.

Исходный код String и StringBuilder (и его закрытый для пакета базовый класс) доступен в src.zip Sun JDK. Вы можете видеть, что вы создаете массив символов (изменяя размер по мере необходимости), а затем выбрасываете его, когда создаете финал String. На практике распределение памяти происходит на удивление быстро.

Обновление: Как отмечает Павел Адамски, производительность изменилась в более позднем HotSpot. javac все еще производит точно такой же код, но компилятор байт-кода обманывает. Простое тестирование полностью терпит неудачу, потому что весь объем кода отбрасывается. Суммирование System.identityHashCode (не String.hashCode) показывает, что код StringBuffer имеет небольшое преимущество. Может быть изменено при выходе следующего обновления или при использовании другой JVM. Из @lukaseder , списка встроенных функций HotSpot JVM .

536
ответ дан 17.09.2019, 09:48
  • 1
    @HyperLink, который Вы видите, что код использует javap -c на скомпилированном классе, который использует его. (О, как в ответе. Просто необходимо интерпретировать дизассемблирование байт-кода, который shouldn' t быть настолько трудным.) – Tom Hawtin - tackline 26.06.2013, 07:58
  • 2
    @Artyom: Я don' t видят что-либо [приблизительно 110] в этом ответе. – Billy ONeal 31.10.2019, 14:18

Я запустил подобный тест как @marcio, но со следующим циклом вместо этого:

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

Просто в придачу, я добавил StringBuilder.append() также. Каждый тест был запущен 10 раз с 100k представителями для каждого выполнения. Вот результаты:

  • StringBuilder без труда побеждает. Результат времени часов был 0 для большинства выполнения, и самое длинное взяло 16 мс.
  • a += b берет приблизительно 40 000 мс (40-е) для каждого выполнения.
  • concat только требует 10 000 мс (10-е) на выполнение.

я не декомпилировал класс, чтобы видеть внутренности или выполнить его через профилировщика все же, но я подозреваю a += b проводит большую часть времени, создавая новые объекты StringBuilder и затем преобразовывая их назад в String.

44
ответ дан 17.09.2019, 09:48
  • 1
    Время создания объекта действительно имеет значение. That' s, почему во многих ситуациях мы используем StringBuilder непосредственно вместо того, чтобы использовать в своих интересах StringBuilder позади +. – coolcfan 02.05.2012, 19:46

Niyaz корректен, но также стоит отметить, что специальное предложение + оператор может быть преобразовано во что-то более эффективное компилятором Java. Java имеет класс StringBuilder, который представляет неориентированную на многопотоковое исполнение, изменяемую Строку. При выполнении набора Конкатенаций строк компилятор Java тихо преобразовывает

String a = b + c + d;

в

String a = new StringBuilder(b).append(c).append(d).toString();

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

Однако concat метод более эффективен при конкатенации пустой строки на существующую Строку. В этом случае JVM не должна создавать новый Строковый объект и может просто возвратить существующий. См. concat документация для подтверждения этого.

Поэтому, если Вы суперобеспокоены эффективностью тогда, что необходимо использовать concat метод при конкатенации возможно-пустых-строк и использования + иначе. Однако различие в производительности должно быть незначительным, и Вы, вероятно, никогда не должны волноваться об этом.

87
ответ дан 17.09.2019, 09:48
  • 1
    concat заражают doesn' t делают это. I' ve отредактировал мое сообщение с декомпиляцией concat метода – shsteimer 07.09.2008, 06:46
  • 2
    заразите его, делает. Посмотрите первые строки своего кода concat. Проблема с concat состоит в том, что он всегда генерирует новую Строку () – Marcio Aguiar 07.09.2008, 07:37
  • 3
    @MarcioAguiar: возможно, Вы подразумеваете, что + всегда генерирует новое String - как Вы говорите, concat имеет одно исключение когда Вы concat пустое String. – Blaisorblade 15.02.2014, 23:02
  • 4
    @Billy ONeal Вы не должны использовать ICU, Повышение. Локаль предоставляет также стандартной библиотеке и Win32 основанные на API бэкенды, которые позволяют Вам использовать то же соглашение о присвоении имен с различными компиляторами (т.е. en_US.UTF-8) как в системах Posix. – Artyom 31.10.2019, 14:17

Tom корректен в описании точно, что + делает оператор. Это создает временный файл StringBuilder, добавляет части и заканчивается с toString().

Однако все ответы до сих пор игнорируют эффекты оптимизации времени выполнения HotSpot. А именно, эти временные операции распознаны как общий шаблон и заменяются более эффективным машинным кодом во времени выполнения.

@marcio: Вы создали микросравнительный тест ; с современной JVM это не допустимый способ представить код.

причина, оптимизация времени выполнения имеет значение, состоит в том, что многие из этих различий в коде - даже включая создание объекта - полностью отличаются, как только HotSpot начинается. Единственный способ знать наверняка представляет Ваш код на месте .

Наконец, все эти методы на самом деле невероятно быстры. Это могло бы быть случаем преждевременной оптимизации. Если у Вас есть код, который конкатенирует, представляет много в виде строки, способ получить максимальную скорость, вероятно, не имеет никакого отношения, какие операторы Вы выбираете и вместо этого алгоритм, который Вы используете!

22
ответ дан 17.09.2019, 09:48
  • 1
    Я предполагаю " эти временные operations" Вы означаете использование escape-анализа выделять " heap" объекты на стеке, где доказуемый корректный. Хотя escape-анализ присутствует в HotSpot (полезен для удаления некоторой синхронизации), я don' t верят ему, во время записи, u – Tom Hawtin - tackline 09.09.2008, 09:48
  • 2
    Я пытался стать хитрым и добавить это к моему маршруту значения по умолчанию AreaRegistration, но этому didn' t работа. Руководство прямо к области = " " работы в обычной ссылке действия. Спасибо. – kmehta 25.02.2020, 18:18

Как насчет некоторого простого тестирования? Используемый код ниже:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • "a + b" версия, выполняемая в [1 113] 2 500 мс .
  • Эти a.concat(b) выполняемый в [1 114] 1 200 мс .

Тестируемый несколько раз. concat() выполнение версии заняло половину времени в среднем.

Этот результат удивил меня, потому что concat() метод всегда создает новую строку (это возвращается" new String(result)". Известно что:

String a = new String("a") // more than 20 times slower than String a = "a"

, Почему компилятор не был способен к, оптимизируют строковое создание в "+ b" код, зная, что это всегда приводило к той же строке? Это могло избежать нового строкового создания. Если Вы не верите оператору выше, протестируйте на Ваш сам.

21
ответ дан 17.09.2019, 09:48
  • 1
    Я предпочитаю creation_date <= TRUNC(SYSDATE) - interval '1' month:) – be here now 28.10.2019, 07:11

Я не думаю так.

a.concat(b) реализован в Строке, и я думаю, что реализация не изменилась очень начиная с ранних машин Java. + операционная реализация зависит от версии Java и компилятора. В настоящее время + реализован с помощью StringBuffer для создания операции максимально быстро. Возможно, в будущем, это изменится. В более ранних версиях операции java + на Строках было намного медленнее, поскольку она привела к промежуточным результатам.

я предполагаю, что += реализован с помощью + и так же оптимизирован.

2
ответ дан 17.09.2019, 09:48
  • 1
    " В настоящее время + реализован с помощью StringBuffer" Ложный It' s StringBuilder. StringBuffer является ориентированным на многопотоковое исполнение impl StringBuilder. – Frederic Morin 07.09.2008, 09:25
  • 2
    Это раньше было StringBuffer до java 1.5, поскольку это было версией, когда StringBuilder был сначала представлен. – ccpizza 15.11.2011, 23:17
  • 3
    Это выглядит прекрасным, но это не применимо для меня как I' m использование его внутренний для частного приложения и это требует, чтобы я купил лицензию (за которым я, вероятно, не могу убедить своего босса кидаться.) Лицензия здесь: sencha.com/license – Pretzel 31.10.2019, 20:34

Оператор + может работать между строкой и строкой, символом, целое число, дважды или значение типа данных float. Это просто преобразовывает значение в свое строковое представление перед конкатенацией.

concat оператор может только быть сделан на и со строками. Это проверяет на совместимость типа данных и бросает ошибку, если они не соответствуют.

Кроме этого, код, который Вы обеспечили, делает тот же материал.

2
ответ дан 17.09.2019, 09:48
  • 1
    Я думаю, что это может быть что I' m поиск. Позвольте мне проверить его и возвратиться к Вам. Спасибо! – Pretzel 31.10.2019, 20:33

Большинство ответов здесь с 2008 года. Похоже, что со временем все изменилось. Мои последние тесты, сделанные с JMH, показывают, что на Java 8 + примерно в два раза быстрее, чем concat.

Мой тест:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}

Результаты:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s
23
ответ дан 17.09.2019, 09:48

Теги

Похожие вопросы