views:

111

answers:

2

i am trying to do a simple string manipulation. input is "murder", i want to get "murderredrum".

i tried this

String str = "murder";
StringBuffer buf = new StringBuffer(str);
// buf is now "murder", so i append the reverse which is "redrum"
buf.append(buf.reverse());
System.out.println(buf);

but now i get "redrumredrum" instead of "murderredrum".

can someone explain what's wrong with my program? thank you.

+11  A: 

The short answer

The line:

buf.append(buf.reverse());

essentially does the following:

buf.reverse();    // buf is now "redrum"
buf.append(buf);

This is why you get "redrumredrum".

That is, buf.reverse() doesn't return a new StringBuffer which is the reverse of buf. It returns buf, after it had reversed itself!

There are many ways to "fix" this, but the easiest would be to explicitly create a new StringBuffer for the reversal, so something like this:

buf.append(new StringBuffer(str).reverse());

Deeper insight: comparing String and StringBuffer

String in Java is immutable. On the other hand, StringBuffer is mutable (which is why you can, among other things, append things to it).

This is why with String, a transforming method really returns a new String. This is why something like this is "wrong"

String str = "murder";
str.toUpperCase(); // this is "wrong"!!!
System.out.println(str); // still "murder"

Instead you want to do:

String str = "murder";
str = str.toUpperCase(); // YES!!!
System.out.println(str); // now "MURDER"!!!

However, the situation is far from analogous with StringBuffer. Most StringBuffer methods do return StringBuffer, but they return the same instance that it was invoked on! They do NOT return a new StringBuffer instance. In fact, you're free to discard the "result", because these methods have already accomplished what they do through various mutations (i.e. side effects) to the instance it's invoked upon.

These methods could've been declared as void, but the reason why they essentially return this; instead is because it facilitates method chaining, allowing you to write something like:

sb.append(thisThing).append(thatThing).append(oneMoreForGoodMeasure);

Related questions


Appendix: StringBuffer vs StringBuilder

Instead of StringBuffer, you should generally prefer StringBuilder, which is faster because it's not synchronized. Most of the discussions above also applies to StringBuilder.

From the documentation:

StringBuffer : A thread-safe, mutable sequence of characters. [...] As of JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder, which should generally be preferred as it supports all of the same operations but faster, as it performs no synchronization.

StringBuilder : A mutable sequence of characters. [...] Instances of StringBuilder are not safe for use by multiple threads. If such synchronization is required then it is recommended that StringBuffer be used.

Related questions


Bonus material! Alternative solution!

Here's an alternative "fix" to the problem that is perhaps more readable:

StringBuilder word = new StringBuilder("murder");
StringBuilder worddrow = new StringBuilder(); // starts empty

worddrow.append(word).append(word.reverse());

System.out.println(worddrow); // "murderredrum"

Note that while this should do fine for short strings, it does use an extra buffer which means that it's not the most efficient way to solve the problem.

Related questions


Bonus material again! The last laugh!

StringBuilder sb = new StringBuilder("ha");
sb.append(sb.append(sb));
System.out.println(sb); // "hahahaha"
polygenelubricants
+1 for the good answer and StringBuilder advise.
Byron Whitlock
+1, StringBuffer is mutable. This might be the OP's confusion, because methods like reverse() return a StringBuffer, sort of like String's methods. But they really mutate the instance and return the instance solely for the convenience of chaining.
GregS
+1  A: 

buf.reverse() gets called first it modifies the stringbuffer to redrum. Now you are appending redrum to redrum

sushil bharwani