2

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.

3
  • In the first scenario, there might actually only be one String in the compiled bytecode, and you don't create a new object. And you don't have to create a new object every time it's invoked. Commented Jun 27, 2014 at 17:30
  • Are you only considering String constants concatenation? or also String variables concatenation? Commented Jun 27, 2014 at 17:39
  • You should consider use the latest in String concatenation: StringBuilder Commented Jun 27, 2014 at 18:19

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

@M.Sharma Your answer doesn't apply here. The first option does not involve String concatenation because those are compile-time constants.
@M.Sharma Added more code to show what's happening. While your article is not incorrect, it does not apply to OP's question.
5

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.

Comments

1

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

1 Comment

You could actually compile the code above and check the bytecode. Your description will not apply to the scenarios in the question.
0

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?

6 Comments

Performance wise, there might not be a concatenation to perform in the first place with the first option.
Does your answer apply to the situation in OP's question?
Not strictly true: stackoverflow.com/questions/1532461/…. Concatenation can be transform into StringBuilder, that is faster than StringBuffer (since no synchronizationà
OP is clearly using that scenario as a sample so it should not be taken as real data. He may also consider String variables concatenation instead of just String constants, in which case, this answer is valid.
@RobertoLinares I don't know about that. They are talking about those two scenarios.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.