Reputation: 4973
I can implement single method in two ways,
1.
public String getTestMessage()
{
return "Hello" + "World..!";
}
2.
public String getTestMessage()
{
return new StringBuffer("Hello").append("World..!").toString();
}
In first scenario, there will be two new String
objects will be created.
In second scenario also there will be two new objects created, but there will be one String
and StringBuffer
. Now way will be fast from them? I am little bit confused.
Upvotes: 2
Views: 703
Reputation: 11877
Between your two scenarios, option 1 will be faster every time, unless the JITC does something I don't expect. I really don't expect it to make a difference, though, unless you call those methods extremely frequently.
Why? Because you actually don't create new objects with option 1. The compiler should perform constant folding, turning "Hello" + "World..!"
into "HelloWorld..!"
. And because this is a compile-time constant String
, it's automatically interned into the String
pool at VM startup. So every time that method is called, you're just getting a reference to that "canonical" String
. No object creation is performed.
In option 2, you always create multiple objects -- the StringBuffer
(you should be using StringBuilder
, by the way), the backing char[]
, and the result String
(at the very least). And doing that in a tight loop is not very efficient.
In addition, option 1 is more readable, which is always something you should consider when writing code.
Proof:
Given this test code:
public class Test {
public String getTestMessageA() {
return "Hello" + "World..!";
}
public String getTestMessageB() {
return new StringBuffer("Hello").append("World..!").toString();
}
}
Compiling with javac -XD-printflat
shows us what this code is processed to before compilation to bytecode:
public class Test {
public Test() {
super();
}
public String getTestMessageA() {
return "HelloWorld..!";
}
public String getTestMessageB() {
return new StringBuffer("Hello").append("World..!").toString();
}
}
Notice how "Hello" + "World..!"
was converted at compile time to a single String
. So String
concatenation is not what is happening in the first option.
Now let's look at the bytecode. Here's the constant pool:
Constant pool:
#1 = Methodref #10.#20 // java/lang/Object."<init>":()V
#2 = String #21 // HelloWorld..!
#3 = Class #22 // java/lang/StringBuffer
#4 = String #23 // Hello
#5 = Methodref #3.#24 // java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
#6 = String #25 // World..!
#7 = Methodref #3.#26 // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
#8 = Methodref #3.#27 // java/lang/StringBuffer.toString:()Ljava/lang/String;
Here's the bytecode for option 1:
public java.lang.String getTestMessageA();
Code:
0: ldc #2 // String HelloWorld..!
2: areturn
Well, that's short. As you can see, the JVM loads a constant (ldc
) from the pool and returns it. No object created directly in the method.
Now here's the bytecode for option 2:
public java.lang.String getTestMessageB();
Code:
0: new #3 // class java/lang/StringBuffer
3: dup
4: ldc #4 // String Hello
6: invokespecial #5 // Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
9: ldc #6 // String World..!
11: invokevirtual #7 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
14: invokevirtual #8 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
17: areturn
So this code creates a new StringBuffer
, loads the appropriate constants from the string pool, calls the append()
method for each of them, then calls the toString()
method on the buffer. The toString()
is implemented as such:
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
So option 2 will create two new objects every time you call it, and also executes more instructions. Thus, option 1 will be faster.
Upvotes: 5
Reputation: 1777
Performance wise, StringBuffer is faster when performing concatenations.This is because when you concatenate a String, you are creating a new object (internally) every time since String is immutable.
For smaller strings (appending one or two strings ) There is no problem with your first method, Consider larger this would be slow down the performance.
Come to this scenario:
if you adding
"value" + 5
Java must convert 5 to a String first. If you look at the byte code for this, Java actually invokes String.valueOf(5). Looking into that method, you come to find that Java creates a char array and a String (2 objects) to build that value.
When Java appends 5, it merely invokes append(int) on the StringBuilder. That method just copies the value into the internal character buffer not creating any extra objects.
Inside append
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
This uses native method System.arrayCopy(), here http://www.cafeaulait.org/course/week2/50.html
see this What is the difference between String and StringBuffer in Java?
Upvotes: 0
Reputation: 8956
Curious to know behind the scene I came across this article which can prove us completely wrong.
The article says The +
operator appears innocent, but the bytecode generated produces some surprises. Using a StringBuffer
for concatenation can in fact produce code that is significantly faster than using a String
.
The bytecode generated by String
concatenation inturn creates a StringBuffer
object, then invokes its append
method.After the concatenation is performed on the StringBuffer
object, it must be converted back into a String
. This is done with the call to the toString
method. This method creates a new String
object from the temporary StringBuffer
object.
In summary, String conactenation inturn created three objects:
A String object, A StringBuffer object, A String object.
However, there is no need to create a temporary StringBuffer
in the second case.
Pease refer the article link for complete information
Upvotes: 1
Reputation: 280178
This
"Hello" + "World..!"
is a constant expression. The compiler will do the concatenation and add it as a single String
constant in the byte code. The JVM will therefore only create one String
object for it.
In
return new StringBuffer("Hello").append("World..!").toString();
there are two String
literals. The JVM will create an object for each of those. It will then create a StringBuffer
object and all the corresponding backing char[]
and finally the String
object for the toString()
method return value.
Upvotes: 5