Сколько объектов String будет создано при объединении нескольких строк?
В интервью меня спросили о количестве объектов, которые будут созданы по данной проблеме:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
Я ответил, что в пуле строк будет создано 6 объектов.
3 будет для каждой из трех переменных.
1 будет дляstr1 + str2
(скажемstr
).
1 будет дляstr2 + str3
,
1 будет дляstr + str3
(str = str1 + str2
).
Правильный ли ответ я дал? Если нет, каков правильный ответ?
7 ответов
Любой ответ на ваш вопрос будет зависеть от реализации JVM и используемой в настоящее время версии Java. Я думаю, что это неразумный вопрос в интервью.
Java 8
На моем компьютере с Java 1.8.0_201 ваш фрагмент кода приводит к этому байт-коду
L0
LINENUMBER 13 L0
LDC "First"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "Second"
ASTORE 2
L2
LINENUMBER 15 L2
LDC "Third"
ASTORE 3
L3
LINENUMBER 16 L3
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
что доказывает, что 5 объектов создаются (3 String
литералы *, 1 StringBuilder
, 1 динамически производится String
экземпляр по StringBuilder#toString
).
Java 12
На моей машине с Java 12.0.2 байт-код
// identical to the bytecode above
L3
LINENUMBER 16 L3
ALOAD 1
ALOAD 2
ALOAD 3
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001\u0001"
]
ASTORE 4
который магически меняет "правильный ответ" на 4 объекта, так как нет промежуточных StringBuilder
участвует.
* Давайте копать немного глубже.
12,5. Создание экземпляров нового класса
Новый экземпляр класса может быть неявно создан в следующих ситуациях:
- Загрузка класса или интерфейса, который содержит строковый литерал ( §3.10.5), может создать новый объект String для представления литерала. (Это не произойдет, если ранее была интернирована строка, обозначающая ту же последовательность кодовых точек Unicode.)
Другими словами, когда вы запускаете приложение, в пуле строк уже есть объекты. Вы едва знаете, что они и откуда они (если вы не сканируете все загруженные классы для всех литералов, которые они содержат).
java.lang.String
Класс, несомненно, будет загружен как необходимый класс JVM, а это означает, что все его литералы будут созданы и помещены в пул.
Давайте возьмем случайно выбранный фрагмент из исходного кода String
выберите из него пару литералов, установите точку останова в самом начале нашей программы и проверьте, содержит ли пул эти литералы.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
public String repeat(int count) {
// ...
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
}
...
}
Они действительно есть.
Как интересная находка, у этой фильтрации IDEA есть побочный эффект: подстроки, которые я искал, также были добавлены в пул. Размер пула увеличился на один ( "bytes String"
был добавлен) после того, как я подал заявку this.contains("bytes String")
,
Куда это нас приведет?
Мы понятия не имеем, "First"
был создан и интернирован, прежде чем мы позвоним String str1 = "First";
, поэтому мы не можем твердо заявить, что линия создает новый экземпляр.
С предоставленной информацией, вопрос не может определенно ответить. Как указано в JLS, §15.18.1:
... Чтобы повысить производительность многократного объединения строк, компилятор Java может использовать
StringBuffer
класс или аналогичный метод, чтобы уменьшить количество промежуточных объектов String, которые создаются путем вычисления выражения.
Это означает, что ответ зависит, по крайней мере, от конкретного используемого компилятора Java.
Я думаю, что лучшее, что мы можем сделать, это дать интервал в качестве ответа:
- умный компилятор может сделать вывод, что
str1
вstr3
никогда не используются и складывают конкатенацию во время компиляции, так что только одинString
-объект создан (тот, на который ссылаетсяstr4
) - Максимально разумное количество
String
Создано должно быть 5: по одному для каждогоstr1
вstr3
один дляtmp = str1 + str2
и один дляstr4 = tmp + str3
,
Итак... мой ответ будет "что-то от одного до пяти String
-объекты ". Что касается общего количества объектов, созданных только для этой операции... Я не знаю. Это также может зависеть от того, как именно, например, StringBuffer
реализовано.
Как в стороне: интересно, какова причина того, чтобы задавать такие вопросы. Обычно не нужно заботиться об этих деталях.
Java 8, вероятно, создаст 5 объектов:
- 3 для 3 литералов
- 1
StringBuilder
- 1 для сцепленных
String
С Java 9 все изменилось, хотя и String
конкатенация не использует StringBuilder
больше.
Должно быть 5:
три для трех литералов (присваивается
str1
,str2
а такжеstr3
)один для
str1 + str2
один для
(result from the previous operation) + str3
(назначенstr4
)
Соответствующая реализация Java может объединять строки любым количеством способов, во время выполнения или во время компиляции, требуя любое количество объектов времени выполнения, включая нулевые объекты, если обнаруживает, что результат не нужен во время выполнения.
4-х строковый объект будет создан в пуле строковых констант. 3 для литералов и 1 с конкатенацией.
если мы используем
String s1 = new String("one")
это создаст два объекта, один в постоянном пуле и один в куче памяти.
если мы определим:
String s1 = "one";
String s2 = new String("one");
это создаст два объекта, один в постоянном пуле и один в куче памяти.
Операция конкатенации не создает столько объектов String. Это создаетStringBuilder
а затем добавляет строки. Таким образом, может быть 5 объектов,
3 (переменные) + 1 (sb) + 1 (объединенная строка).