i am trying to do a simple string manipulation. input is "murder", i want to get "murderredrum".
i tried this
String str = "murder";
StringBu开发者_开发问答ffer 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.
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
- Method chaining - why is it a good practice, or not?
- Fluent Interfaces - Method Chaining
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 ofStringBuilder
are not safe for use by multiple threads. If such synchronization is required then it is recommended thatStringBuffer
be used.
Related questions
- StringBuilder and StringBuffer in Java
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
- Reverse a string in Java, in O(1)? - as a
CharSequence
, yes this can be done!
Bonus material again! The last laugh!
StringBuilder sb = new StringBuilder("ha");
sb.append(sb.append(sb));
System.out.println(sb); // "hahahaha"
buf.reverse() gets called first it modifies the stringbuffer to redrum. Now you are appending redrum to redrum
精彩评论