Подтвердить что ты не робот

В чем разница при конкатенации String как переменной с символом против конкатенации с другой строкой?

Когда я вижу что-то (псевдо-1-лайнер) следующим образом:

str1 + "a" + str2

Является ли это намного хуже (или лучше/равно), чем следующее (псевдо 1-лайнер)?

str1 + 'a' + str2

Обновление: Лучший пример (by @QPaysTaxes), чтобы уменьшить путаницу в отношении моего первоначального примера.

Что я пробовал: Различные материалы за последние 10 лет программируют Java, но я никогда не мог понять, что находится под капотом - например. я бы предположил, второй немного "быстрее/лучше", потому что нет строковых объектов, созданных для косой черты, и/или сборщик мусора Java должен обрабатывать меньше. Я когда-то готовился к сертификатам Java, и, возможно, смог бы вернуться к этому времени еще лучше, но, похоже, мой ежедневный бизнес "теория" о Java тоже должен быть обновлен. Я знаю без каких-либо лучше, чем мое предположение о том, что indexOf('c') следует использовать, а не indexOf("C"), и я задавался вопросом, совпадают ли те же значения для String-конкатенации.

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

Что я попробую: На основе принятого ответа здесь Конкатенация строк: concat() vs "+" оператор, я надеюсь, что смогу начать видеть, что находится под капотом, и в один прекрасный день сможет спорить/отвечать на такие вопросы, которые требуют.

4b9b3361

Ответ 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 для конкатенации строк намного проще в памяти, чем построение строк с помощью оператора +.