How many String objects would be created when concatenating multiple Strings?
With the given information, the question cannot be definitely answered. As is stated in the JLS, §15.18.1:
... 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.
This means that the answer depends at least on the concrete Java compiler used.
I think the best we can do is give an interval as answer:
- a smart compiler may be able to infer that
str1
tostr3
are never used and fold the concatenation during compilation, such that only oneString
-object is created (the one referenced bystr4
) - The maximum sensible number of
String
s created should be 5: one each forstr1
tostr3
, one fortmp = str1 + str2
and one forstr4 = tmp + str3
.
So... my answer would be "something between one to five String
-objects". As to the total number of objects created just for this operation... I do not know. This may also depend how exactly e.g. StringBuffer
is implemented.
As an aside: I wonder what the reason behind asking such questions is. Normally, one does not need to care about those details.
Any answer to your question will depend on the JVM implementation and the Java version currently being used. I think it's an unreasonable question to ask in an interview.
Java 8
On my machine, with Java 1.8.0_201, your snippet results in this bytecode
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
which proves that 5 objects are being created (3 String
literals*, 1 StringBuilder
, 1 dynamically produced String
instance by StringBuilder#toString
).
Java 12
On my machine, with Java 12.0.2, the bytecode is
// 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
which magically changes "the correct answer" to 4 objects since there is no intermediate StringBuilder
involved.
*Let's dig a bit deeper.
12.5. Creation of New Class Instances
A new class instance may be implicitly created in the following situations:
- Loading of a class or interface that contains a string literal (§3.10.5) may create a new String object to represent the literal. (This will not occur if a string denoting the same sequence of Unicode code points has previously been interned.)
In other words, when you start an application, there are already objects in the String pool. You barely know what they are and where they come from (unless you scan all loaded classes for all literals they contain).
The java.lang.String
class will be undoubtedly loaded as an essential JVM class, meaning all its literals will be created and placed into the pool.
Let's take a randomly selected snippet from the source code of String
, pick a couple of literals from it, put a breakpoint at the very beginning of our programme, and examine if the pool contains these literals.
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.");
}
}
...
}
They are there indeed.
As an interesting find, this IDEA's filtering has a side effect: the substrings I was looking for have been added to the pool as well. The pool size increased by one ("bytes String"
was added) after I applied this.contains("bytes String")
.
Where does this leave us?
We have no idea whether "First"
was created and interned before we call String str1 = "First";
, so we can't state firmly that the line creates a new instance.