Нет, не совсем.
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 .
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 метод при конкатенации возможно-пустых-строк и использования + иначе. Однако различие в производительности должно быть незначительным, и Вы, вероятно, никогда не должны волноваться об этом.
Я запустил подобный тест как @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
.
Большинство ответов здесь с 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
Tom корректен в описании точно, что + делает оператор. Это создает временный файл StringBuilder
, добавляет части и заканчивается с toString()
.
Однако все ответы до сих пор игнорируют эффекты оптимизации времени выполнения HotSpot. А именно, эти временные операции распознаны как общий шаблон и заменяются более эффективным машинным кодом во времени выполнения.
@marcio: Вы создали микросравнительный тест ; с современной JVM это не допустимый способ представить код.
причина, оптимизация времени выполнения имеет значение, состоит в том, что многие из этих различий в коде - даже включая создание объекта - полностью отличаются, как только HotSpot начинается. Единственный способ знать наверняка представляет Ваш код на месте .
Наконец, все эти методы на самом деле невероятно быстры. Это могло бы быть случаем преждевременной оптимизации. Если у Вас есть код, который конкатенирует, представляет много в виде строки, способ получить максимальную скорость, вероятно, не имеет никакого отношения, какие операторы Вы выбираете и вместо этого алгоритм, который Вы используете!
Как насчет некоторого простого тестирования? Используемый код ниже:
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" код, зная, что это всегда приводило к той же строке? Это могло избежать нового строкового создания. Если Вы не верите оператору выше, протестируйте на Ваш сам.
Оператор + может работать между строкой и строкой, символом, целое число, дважды или значение типа данных float. Это просто преобразовывает значение в свое строковое представление перед конкатенацией.
concat оператор может только быть сделан на и со строками. Это проверяет на совместимость типа данных и бросает ошибку, если они не соответствуют.
Кроме этого, код, который Вы обеспечили, делает тот же материал.
Я не думаю так.
a.concat(b)
реализован в Строке, и я думаю, что реализация не изменилась очень начиная с ранних машин Java. +
операционная реализация зависит от версии Java и компилятора. В настоящее время +
реализован с помощью StringBuffer
для создания операции максимально быстро. Возможно, в будущем, это изменится. В более ранних версиях операции java +
на Строках было намного медленнее, поскольку она привела к промежуточным результатам.
я предполагаю, что +=
реализован с помощью +
и так же оптимизирован.