В чем разница при конкатенации String как переменной с символом против конкатенации с другой строкой?
Когда я вижу что-то (псевдо-1-лайнер) следующим образом:
str1 + "a" + str2
Является ли это намного хуже (или лучше/равно), чем следующее (псевдо 1-лайнер)?
str1 + 'a' + str2
Обновление: Лучший пример (by @QPaysTaxes), чтобы уменьшить путаницу в отношении моего первоначального примера.
Что я пробовал:
Различные материалы за последние 10 лет программируют Java, но я никогда не мог понять, что находится под капотом - например. я бы предположил, второй немного "быстрее/лучше", потому что нет строковых объектов, созданных для косой черты, и/или сборщик мусора Java должен обрабатывать меньше.
Я когда-то готовился к сертификатам Java, и, возможно, смог бы вернуться к этому времени еще лучше, но, похоже, мой ежедневный бизнес "теория" о Java тоже должен быть обновлен. Я знаю без каких-либо лучше, чем мое предположение о том, что indexOf('c')
следует использовать, а не indexOf("C")
, и я задавался вопросом, совпадают ли те же значения для String-конкатенации.
Я тоже немного поработал, но поскольку мой заголовок может означать, что я не очень хорошо описываю то, что ищу, без примера. Я сожалею об этом, и возможность этого гандикапа просто продублировала дубликат.
Что я попробую:
На основе принятого ответа здесь Конкатенация строк: concat() vs "+" оператор, я надеюсь, что смогу начать видеть, что находится под капотом, и в один прекрасный день сможет спорить/отвечать на такие вопросы, которые требуют.
Ответы
Ответ 1
Основываясь на принятом ответе, я надеюсь, что смогу начать посмотрите, что находится под капотом.
Посмотрим на сгенерированный байт-код при конкатенации строки с символом:
String str1 = "a" + "test";
String str2 = 'a' + "test";
0: ldc #2 // String atest
2: astore_1
3: ldc #2 // String atest
5: astore_2
как вы можете видеть, нет никакой разницы, компилятор преобразует его в тот же байт-код.
Теперь давайте посмотрим на сгенерированный байт-код при конкатенации символа в переменную String.
String str1 = "a" + str3; //str3 is a String
String str2 = 'a' + str3;
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String a
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: new #3 // class java/lang/StringBuilder
26: dup
27: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
30: bipush 97
32: invokevirtual #8 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
35: aload_1
36: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
Как вы можете видеть, есть небольшая разница.
10: ldc #5 // String a
ldc
нажмите константу #index из пула констант (String, int или float) в стек.
Поэтому, если вы напрямую конкатенации с переменной, конкатенирование символа будет генерировать меньше байт-кода, то есть под капотом.
Теперь для проблемы с производительностью это не будет означать какую-либо значимую разницу в производительности, поскольку компилятор JIT оптимизирует большинство временных объектов, если вы не указали при запуске вашей программы, чтобы отключить компилятор JIT с помощью -Djava.compiler=NONE
.
Ответ 2
Я предпочитаю использовать "a"
вместо 'a'
, чтобы убедиться, что результатом является String
.
Рассмотрим это:
public static void main(String... args) {
String s = "foo";
int i = 1;
Object arg = s + '/' + i;
log(arg);
}
private static void log(Object... args) {
MessageFormat format = new MessageFormat("bar {0}");
String message = format.format(args);
System.out.println(message); // or write to a log or something
}
Предположим, вы решили, что вам больше не понадобится s
в сообщении и измените третью строку в методе main
на:
Object arg = '/' + i;
Тогда arg
будет содержать только число, потому что char + int
не объединяется, а добавляет значения.
Ответ 3
Если вы создадите имя файла, вы обязательно его примете впоследствии. Это в большинстве случаев подразумевает доступ к физическому медиа, который является величинами медленнее, чем все, что вы можете сделать неправильно, соединяя ваши строки. Итак, делайте то, что является основным, и не беспокойтесь о производительности в этом конкретном случае.
Мой совет при создании имен файлов заключается в использовании класса File
или Path
, который автоматически обеспечит правильное удаление разделителей путей.
EDIT: Как вы указываете в своем комментарии, ваш вопрос касается общего случая. Посмотрите на источник. StringBuilder.append(String)
заканчивает выполнение System.arraycopy()
в String.getChars()
, а StringBuilder.append(char)
копирует только один символ. Поэтому теоретически StringBuilder.append(char)
будет быстрее.
Однако вам нужно будет сравнить это, чтобы узнать, не имеет ли на практике никаких изменений.
Ответ 4
Я не уверен, что любой из вариантов лучше с точки зрения производительности, но я могу подумать о другой проблеме, которая должна была бы рассмотреть, что предпочло бы первый фрагмент.
Компилятор может лучше защитить вас от опечаток, если вы добавляете примитивы вместо строкового представления этих примитивов.
Рассмотрим:
String plus10 = "plus" + 10;
Если вы вводите по ошибке
String plus10 = "plus" + 1O;
Компилятор даст вам сообщение об ошибке.
Если, с другой стороны, вы вводите
String plus10 = "plus" + "1O";
У компилятора не будет проблем с этим.
То же самое касается добавления char
s
String plus = "x" + '++' + "y";
не будет компилироваться, пока
String plus = "x" + "++" + "y";
передаст компиляцию.
Конечно, было бы лучше использовать константы, а не жестко закодированные значения (и добавлять к StringBuilder
вместо использования String
конкатенации), но даже для констант я предпочел бы примитивные типы поверх строк, поскольку они дают вам еще один уровень защиты от ошибок.
Ответ 5
На самом деле нет никакой существенной разницы в производительности. В среднем для выполнения конкатенации строк потребуется одно и то же время.
Однако внутренний компилятор Java заменяет оператор +
StringBuilder
во время компиляции.
Поэтому при использовании оператора +
с char компилятор будет преобразовывать его в StringBuilder
внутри себя и использовать .append(char)
. То же самое произойдет со строкой, с той разницей, что она будет использовать .append(String)
.
И, как я уже упоминал выше, нет никакой разницы в среднем. Простой тест покажет, что разница во времени близка к 0. Так что это действительно вопрос читаемости. И с точки зрения читаемости, если вы концентрируете строки, лучше сохранить тип одинаковым и использовать String даже для одиночных символов, а не char
Ответ 6
Глядя на исходный код, часто помогает понять, что происходит.
String s = s1 + s2
Выполняется:
String s = new StringBuilder(s1).append(s2).toString();
Теперь загляните в исходный код append (char) и добавьте (строку) класса StringBuilder
:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28char%29
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28java.lang.String%29
Вы увидите, что append (string) выполняет больше проверок, чтобы увидеть, является ли строка пустой или пустой. Однако вы, вероятно, не заметите разницы.
Ответ 7
Это то, что под капотом: String str = s1 + "/";
по существу создает 2 новых отдельных объекта String (str
и new String("/"))
.
Это не проблема для небольшого программного обеспечения, но подумайте об этом по соображениям памяти, если бы вы создали 2 объекта String (имейте в виду: объекты сохраняют 1 запись в Stack плюс содержимое, хранящееся в куче) для n > 500 000 базы данных записей.
Использование одинарных кавычек, таких как String str = s1 + '/'
, приведет к другому процессу целиком. '/'
означает числовое значение символьного представления ASCii любого символа одиночного, написанного между кавычками. Эта операция имеет постоянную (O (1)) время выполнения (думаю, доступ к мгновенному массиву) и, естественно, будет быстрее, чем создание и привязка объектов.
Как уже было предложено многими людьми, использование объекта StringBuilder
для конкатенации строк намного проще в памяти, чем построение строк с помощью оператора +.