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

  1. Напишите свою собственную библиотеку AOP.
  2. Используйте рефлексию для создания прокси-сервера регистрации через свои экземпляры (не уверен, что вы можете сделать это, не изменяя часть вашего существующего кода).
  3. Перепишите сборку и введите код регистрации (в основном то же самое, что и 1).
  4. Устанавливайте CLR и добавьте ведение журнала на этом уровне (я думаю, что это самое сложное решение для реализации, не уверен, что у вас есть необходимые крючки в CLR, хотя).
460
задан Peter Mortensen 5 June 2017 в 21: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 .

531
ответ дан Tom Hawtin - tackline 5 June 2017 в 21:10
поделиться

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
ответ дан Community 5 June 2017 в 21:10
поделиться

Я запустил подобный тест как @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
ответ дан ckpwong 5 June 2017 в 21:10
поделиться

Большинство ответов здесь с 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
ответ дан Paweł Adamski 5 June 2017 в 21:10
поделиться

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

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

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

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

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

22
ответ дан Jason Cohen 5 June 2017 в 21:10
поделиться

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

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
ответ дан acdcjunior 5 June 2017 в 21:10
поделиться

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

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

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

2
ответ дан dim8 5 June 2017 в 21:10
поделиться

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

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

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

2
ответ дан Adrien Brunelat 5 June 2017 в 21:10
поделиться
Другие вопросы по тегам:

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