I was asked in an interview about the number of objects that will be created on the given problem:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
I answered that there would be 6 objects created in the string pool.
3 would be for each of the three variables.
1 would be forstr1 + str2
(let's saystr
).
1 would be forstr2 + str3
.
1 would be for thestr + str3
(str = str1 + str2
).
Is the answer I gave correct? If not, what is the correct answer?
The answer is: 2 String objects are created.
You concatenate strings by using the + operator. For string literals and string constants, concatenation occurs at compile time; no run-time concatenation occurs. For string variables, concatenation occurs only at run time.
Concatenation means joining strings together end-to-end to create a new string. To concatenate strings, we use the + operator. Keep in mind that when we work with numbers, + will be an operator for addition, but when used with strings it is a joining operator.
Since the String object is immutable, each call for concatenation will result in a new String object being created.
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.
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
).
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.
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:
str1
to str3
are never used and fold the concatenation during compilation, such that only one String
-object is created (the one referenced by str4
)String
s created should be 5: one each for str1
to str3
, one for tmp = str1 + str2
and one for str4 = 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.
Java 8 will likely create 5 objects:
StringBuilder
String
With Java 9 things changed though and String
concatenation does not use StringBuilder
anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With