Vishal Zanzrukia
Vishal Zanzrukia

Reputation: 4973

StringBuffer and String usage scenario in Java

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

Answers (4)

awksp
awksp

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

saravanakumar
saravanakumar

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

SparkOn
SparkOn

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

Sotirios Delimanolis
Sotirios Delimanolis

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

Related Questions