Предполагая, что строки 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);
}
}
Не уверен, что + можно декомпилировать.
Используйте javap для дизассемблирования файла класса Java.
Из-за `` неизменности '' вам, вероятно, следует использовать StringBuffer или StringBuilder- (поток небезопасен, поэтому быстрее, вместо этого




+ оператор может работать между строкой и значением типа данных string, char, integer, double или float. Он просто преобразует значение в его строковое представление перед конкатенацией.
оператор concat может выполняться только со строками. Он проверяет совместимость типов данных и выдает ошибку, если они не совпадают.
За исключением этого, предоставленный вами код делает то же самое.
Я так не думаю.
a.concat(b) реализован в String, и я думаю, что реализация не сильно изменилась с ранних Java-машин. Реализация операции + зависит от версии Java и компилятора. В настоящее время + реализован с использованием StringBuffer, чтобы сделать операцию как можно быстрее. Возможно, в будущем это изменится. В более ранних версиях Java + операция со строками была намного медленнее, так как приводила к промежуточным результатам.
Я предполагаю, что += реализован с использованием + и оптимизирован аналогичным образом.
«В настоящее время + реализован с использованием StringBuffer» False Это StringBuilder. StringBuffer - это потокобезопасный вариант StringBuilder.
Раньше это был StringBuffer до java 1.5, так как это была версия, когда впервые был представлен StringBuilder.
Нияз является правильным, но также стоит отметить, что специальный оператор + может быть преобразован компилятором Java во что-то более эффективное. В Java есть класс StringBuilder, который представляет непоточно изменяемую строку. При выполнении группы конкатенаций строк компилятор Java незаметно преобразует
String a = b + c + d;
в
String a = new StringBuilder(b).append(c).append(d).toString();
что для больших струн значительно эффективнее. Насколько мне известно, этого не происходит при использовании метода concat.
Однако метод concat более эффективен при объединении пустой строки с существующей строкой. В этом случае JVM не нужно создавать новый объект String, и она может просто вернуть существующий. См. документация по concat, чтобы подтвердить это.
Поэтому, если вас очень беспокоит эффективность, вам следует использовать метод concat при объединении возможно пустых строк и использовать + в противном случае. Однако разница в производительности должна быть незначительной, и вам, вероятно, не стоит об этом беспокоиться.
concat infact этого не делает. Я отредактировал свой пост декомпиляцией метода concat
на самом деле это так. Посмотрите первые строки вашего кода concat. Проблема с concat в том, что он всегда генерирует новый String ()
@MarcioAguiar: возможно, вы имеете в виду, что + всегда генерирует новый String - как вы говорите, у concat есть одно исключение, когда вы объединяете пустой String.
Нет, не совсем так.
Во-первых, есть небольшая разница в семантике. Если 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 (и его базовый класс package-private) доступен в src.zip Sun JDK. Вы можете видеть, что вы создаете массив символов (изменяя размер по мере необходимости), а затем выбрасываете его, когда создаете окончательный String. На практике распределение памяти происходит на удивление быстро.
Обновлять: Как отмечает Павел Адамски, в более позднем HotSpot производительность изменилась. javac по-прежнему производит точно такой же код, но компилятор байт-кода обманывает. Простое тестирование полностью не выполняется, потому что весь код отбрасывается. Суммирование System.identityHashCode (не String.hashCode) показывает, что код StringBuffer имеет небольшое преимущество. Возможны изменения при выходе следующего обновления или при использовании другой JVM. Из @lukaseder, список встроенных функций HotSpot JVM.
@HyperLink Вы можете увидеть код, использующий javap -c в скомпилированном классе, который его использует. (О, как в ответе. Вам просто нужно интерпретировать разборку байт-кода, что не должно быть так сложно.)
Вы можете обратиться к Спецификация JVM, чтобы понять отдельные байт-коды. Материал, на который вы хотели бы сослаться, находится в главе 6. Немного неясно, но вы можете довольно легко понять суть.
Интересно, почему компилятор Java использует StringBuilder даже при соединении двух строк? Если String включал статические методы для объединения до четырех строк или всех строк в String[], код мог бы добавить до четырех строк с двумя выделениями объектов (результат String и его поддержка char[], ни одна из которых не является избыточной), и любое количество строк. с тремя выделениями (String[], результат String и резервный char[], причем только первое является избыточным). Как бы то ни было, использование StringBuilder потребует для в лучшем случае четырех выделений и потребует дважды копировать каждый символ.
Это выражение a + = b. Разве это не значит: a = a + b?
@ user132522 Да. (Хотя с другими типами может быть задействовано приведение типов.)
Как это связано с конкатенацией? Вы имеете в виду, что a - строковый объект, а b - другого типа. Итак, b вынужден стать строковым объектом?
Это то, что вы имеете в виду под кастингом? Я на элементарном уровне. Я обычно использую слово «приведение» для преобразования между int и double (усечение).
@ user132522 Для таких типов, как short, результатом + будет int (потому что это такая странная вещь, которую делает Java). Таким образом, a = a + b; не будет компилироваться, поскольку вы не можете назначить int для short. a += b; будет молча возвращаться к short.
Таким образом, int = short + short не будет компилироваться. Но b / c сокращение приводит к сокращению результата int. В формате short + = short будет скомпилировано то же выражение. Я правильно понимаю?
Об этом StringBuilder лучше, чем concat: поправьте меня, если я ошибаюсь, но StringBuilder и другие ему подобные выделяют в 2 раза больше памяти для получения нового контента, который превышает потребность в емкости. Построение SQL-запросов я получал несколько раз ошибку, по этой причине в тестах, и я решил использовать «concat», было немного менее удобно, но память была хорошей.
Все изменилось с тех пор, как был создан этот ответ. Пожалуйста, прочтите мой ответ ниже.
Не могли бы вы подробнее рассказать о части "читы для компилятора байт-кода"?
Как насчет простого тестирования? Использовал приведенный ниже код:
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" выполняется в 2500 мс.a.concat(b) выполняется в 1200 мс.Проверено несколько раз. Выполнение версии concat() заняло в среднем половину времени.
Этот результат меня удивил, потому что метод concat() всегда создает новую строку (возвращает «new String(result)». Хорошо известно, что:
String a = new String("a") // more than 20 times slower than String a = "a"
Почему компилятор не смог оптимизировать создание строки в коде "a + b", зная, что это всегда приводит к одной и той же строке? Это могло избежать создания новой строки. Если вы не верите в вышеприведенное утверждение, проверьте себя.
Я тестировал ваш код на java jdk1.8.0_241. Для меня код «a + b» дает оптимизированные результаты. С concat (): 203 мс и с «+»: 113 мс. Думаю, в предыдущем выпуске он не был оптимизирован.
Том правильно описывает, что делает оператор +. Он создает временный StringBuilder, добавляет детали и завершает работу с toString().
Однако все ответы до сих пор игнорируют эффекты оптимизации времени выполнения HotSpot. В частности, эти временные операции распознаются как общий шаблон и заменяются более эффективным машинным кодом во время выполнения.
@marcio: Вы создали микро-тест; с современными JVM это недопустимый способ профилирования кода.
Причина, по которой оптимизация времени выполнения имеет значение, заключается в том, что многие из этих различий в коде - даже включая создание объектов - полностью различаются после запуска HotSpot. Единственный способ узнать наверняка - это профилировать ваш код на месте.
Наконец, все эти методы на самом деле невероятно быстрые. Это может быть случай преждевременной оптимизации. Если у вас есть код, который часто объединяет строки, способ достижения максимальной скорости, вероятно, не имеет ничего общего с тем, какие операторы вы выбираете, а вместо этого с алгоритмом, который вы используете!
Я предполагаю, что под «этими временными операциями» вы подразумеваете использование анализа выхода для размещения объектов «кучи» в стеке, где это доказуемо правильно. Хотя в HotSpot есть escape-анализ (полезный для удаления некоторой синхронизации), я не верю, что на момент написания статьи u
Я провел аналогичный тест, как @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 раз, по 100 тысяч повторений в каждом. Вот результаты:
StringBuilder безоговорочно побеждает. Результатом времени на часах было 0 для большинства прогонов, а самый длинный результат занял 16 мс.a += b занимает около 40000 мс (40 с) на каждый запуск.concat требует всего 10000 мс (10 с) на запуск.Я еще не декомпилировал класс, чтобы увидеть внутреннее устройство или запустить его через профилировщик, но я подозреваю, что a += b тратит большую часть времени на создание новых объектов StringBuilder, а затем на преобразование их обратно в String.
Время создания объекта действительно имеет значение. Вот почему во многих ситуациях мы используем StringBuilder напрямую, а не пользуемся преимуществами StringBuilder позади +.
@coolcfan: Когда + используется для двух строк, есть ли случаи, когда использование StringBuilder лучше, чем String.valueOf(s1).concat(s2)? Есть идеи, почему компиляторы не используют последний [или опускают вызов valueOf в тех случаях, когда известно, что s1 не равен нулю]?
@supercat извини, я не знаю. Может быть, лучше всего на это ответят люди, стоящие за этим сахаром.
Искать: invokedynamic StringConcatFactory
По сути, есть два важных различия между + и методом concat.
Если вы используете метод concat, тогда вы сможете только объединить строки, в то время как в случае оператора + вы также можете объединить строку с любым типом данных.
Например:
String s = 10 + "Hello";
В этом случае на выходе должно быть 10Привет.
String s = "I";
String s1 = s.concat("am").concat("good").concat("boy");
System.out.println(s1);
В приведенном выше случае вы должны указать две обязательные строки.
Второе и главное различие между + и concat заключается в том, что:
Случай 1: Предположим, я объединяю те же строки с оператором concat таким образом
String s = "I";
String s1=s.concat("am").concat("good").concat("boy");
System.out.println(s1);
В этом случае общее количество объектов, созданных в пуле, равно 7:
I
am
good
boy
Iam
Iamgood
Iamgoodboy
Случай 2:
Теперь я собираюсь объединить те же строки с помощью оператора +
String s = "I"+"am"+"good"+"boy";
System.out.println(s);
В приведенном выше случае общее количество созданных объектов составляет всего 5.
Фактически, когда мы объединяем строки с помощью оператора +, тогда он поддерживает класс StringBuffer для выполнения той же задачи следующим образом: -
StringBuffer sb = new StringBuffer("I");
sb.append("am");
sb.append("good");
sb.append("boy");
System.out.println(sb);
Таким образом будет создано только пять объектов.
Итак, ребята, это основные различия между методом + и concat. Наслаждаться :)
Дорогой мой, Вы очень хорошо знаете, что любой строковый литерал рассматривается как сам объект String, который хранится в пуле String. Итак, в этом случае у нас есть 4 строковых литерала. Очевидно, что в пуле должно быть создано как минимум 4 объекта.
Я так не думаю: String s = "I"+"am"+"good"+"boy"; String s2 = "go".concat("od"); System.out.println(s2 == s2.intern()); печатает true, что означает, что "good" не был в пуле строк до вызова intern()
Я говорю только об этой строке String s = «Я» + «am» + «good» + «boy»; В этом случае все 4 строковых литерала хранятся в пуле, поэтому в пуле должны быть созданы 4 объекта.
При использовании + скорость уменьшается по мере увеличения длины строки, но при использовании concat скорость более стабильна, и лучший вариант - использовать класс StringBuilder, который имеет стабильную скорость для этого.
Думаю, вы понимаете почему. Но лучший способ создания длинных строк - использовать StringBuilder () и append (), любая скорость будет неприемлемой.
использование оператора + эквивалентно использованию StringBuilder (docs.oracle.com/javase/specs/jls/se8/html/…)
@ihebiheb или StringBuffer
@ihebiheb "Реализация оператора конкатенации строк оставлена на усмотрение компилятора Java, если компилятор в конечном итоге соответствует спецификации языка Java ™. Например, компилятор javac май реализует оператор с помощью StringBuffer, StringBuilder или java.lang.invoke.StringConcatFactory в зависимости от версии JDK (...) ". docs.oracle.com/en/java/javase/11/docs/api/java.base/java/la ng /…
Для полноты картины я хотел добавить, что определение оператора '+' можно найти в JLS SE8 15.18.1:
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
The result of string concatenation is a reference to a String object that is the concatenation of the two operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in the newly created string.
The String object is newly created (§12.5) unless the expression is a constant expression (§15.28).
О реализации JLS говорит следующее:
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive types, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
Итак, судя по тому, что «компилятор Java может использовать класс StringBuffer или аналогичный метод для сокращения», разные компиляторы могут создавать разный байт-код.
Большинство ответов здесь из 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
Интересно, почему Java String никогда не включал статическую функцию для формирования строки путем объединения элементов String[]. Использование + для объединения 8 строк с использованием такой функции потребует создания и последующего отказа от String[8], но это будет единственный объект, который нужно будет построить заброшенным, в то время как использование StringBuilder потребует создания и отказа от экземпляра StringBuilder и по меньшей мере одного резервного хранилища char[] .
@supercat Некоторые статические методы String.join() были добавлены в Java 8 в качестве быстрой синтаксической оболочки для класса java.util.StringJoiner.
@TiStrga: Изменилось ли обращение с + для использования таких функций?
@supercat Это нарушит бинарную обратную совместимость, так что нет. Это было просто в ответ на ваш комментарий «почему String никогда не включал статическую функцию»: теперь есть такая функция является. Остальная часть вашего предложения (рефакторинг + для его использования), к сожалению, потребует большего, чем то, что разработчики Java готовы изменить.
@TiStrga: Есть ли способ, которым файл байт-кода Java может указывать: «Если функция X доступна, вызовите ее; в противном случае сделайте что-нибудь еще» таким образом, чтобы это могло быть разрешено в процессе загрузки класса? Создание кода с помощью статического метода, который может либо связываться со статическим методом Java, либо использовать построитель строк, если он недоступен, может показаться оптимальным решением.
Не могли бы вы мне сказать, почему + быстрее StringBuilder в 2 раза?
С Java 9. Все снова изменилось. Пожалуйста, обновите.
возможный дубликат StringBuilder против конкатенации строк в toString () в Java