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.

Vishal Zanzrukia
  • 4,902
  • 4
  • 38
  • 82
  • 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. – awksp Jun 27 '14 at 17:30
  • Are you only considering String constants concatenation? or also String variables concatenation? – Roberto Linares Jun 27 '14 at 17:39
  • You should consider use the latest in String concatenation: `StringBuilder` – Josef E. Jun 27 '14 at 18:19

4 Answers4

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.

awksp
  • 11,764
  • 4
  • 37
  • 44
  • @M.Sharma Your answer doesn't apply here. The first option does *not* involve `String` concatenation because those are compile-time constants. – awksp Jun 27 '14 at 18:15
  • @M.Sharma Added more code to show what's happening. While your article is not incorrect, it does not apply to OP's question. – awksp Jun 27 '14 at 18:25
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.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
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

SparkOn
  • 8,806
  • 4
  • 29
  • 34
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?

Community
  • 1
  • 1
saravanakumar
  • 1,747
  • 4
  • 20
  • 38
  • Performance wise, there might not *be* a concatenation to perform in the first place with the first option. – awksp Jun 27 '14 at 17:30
  • 2
    Does your answer apply to the situation in OP's question? – Sotirios Delimanolis Jun 27 '14 at 17:30
  • Not strictly true: http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java. Concatenation can be transform into StringBuilder, that is faster than StringBuffer (since no synchronizationà – Mik378 Jun 27 '14 at 17:31
  • 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. – Roberto Linares Jun 27 '14 at 17:38
  • @RobertoLinares I don't know about that. They are talking about those two scenarios. – Sotirios Delimanolis Jun 27 '14 at 17:44
  • @VishalZanzrukia , Sotirios Delimanolis,Roberto Linares Hi All thanks for your command, I blindly compare performance of String and StringBuffer not for this particular scenario. Sorry! – saravanakumar Jun 27 '14 at 18:01