It's immensely frustrating, but StringBuffer doesn't override hashCode(), so the value you get is a constant for a given object and is not dependent on the characters in the buffer. This is one of those favorite little "trick questions" on the exam.
Originally posted by Bilal Sallakh: Two StringBuffers can't be considered equal even if they contain the same string since each is inherently vulenrable to change.
That's irrelevant. Plenty of mutable classes implement equals() and hashCode() based on their changeable state -- all of the Collections classes, for example. This was an oversight in the original API; by the time it was pointed out, it couldn't be corrected without breaking existing code. No more, no less than a simple mistake.
It's true that they could have made StringBuffer's equals() and hashCode() depend on the contents as they do for String. But what benefit would this serve? Particularly for hashCode() - why would we need the hashCode() of a StringBuffer? Is it being used as a key in a HashMap? If so, there are two basic possibilities: (a) we don't anticipate needing to change the contents of the StringBuffer after using it as a key, or (b) we do plan to change the contents. If it's (a) - then why not simply use toString() to get a nice immutable key with the desired proerties? If it's (b) - that seems like a bad, bad idea to me. If the hashCode() changes after an instance has been used as a key, the HashMap probably won't pick up the change right away. The instance will still be in the old location in the map. So if someone does a get() using the new instance, it will probably fail, even though the instance is a key in the map. Unless, of course, the HashMap gets rehashed at some point, at which point the instance will move to its new "correct" location in the map. Basically, all this means that if keys change after insertion, a HashMap will behave very unpredictably. Best procatice IMO is either use immutable classes for keys, or if you must use a mutable class, make sure you don't actually change the instances after using them as keys.
[EFH]: Plenty of mutable classes implement equals() and hashCode() based on their changeable state -- all of the Collections classes, for example.
Yes, but most of those classes don't come in two flavors - mutable and immutable - they way String and StringBuffer do. Given that many classes need to be mutable for some applications, but not for others, it would be a bit of a pain to make mutable and immutable versions of all our classes. (Though Collections does provide us with immutable decorators we can use for this purpose if we like.) So Ok, most classes are mutable, and since we also sometimes want to use them in a HashMap, they also have hashCode() methods that depend on instance data. We just have to rely on programmer discipline not to use mutator methods after using an instance as a key. Hurm, doesn't sound very trustworty to me. But for many existing classes in Java, it's the best we can do, usually.
In the case of String and StringBuffer though, I would argue that the only reason to use a StringBuffer is if you're planning to modify it. And if you're not (anymore), there's no reason not to use a String from that point out. If you're using a StringBuffer as a hash key, something's probably wrong somewhere. I'd be perfectly happy if hashCode() threw an UnsupportedMethodOperation at this point - the programmer should be notified that something's wrong, IMO.
Does that make sense, or have I overlooked some valid potential use of StringBuffer's hashCode()?
By the way, I like Ruby's solution to this problem. All classes have a freeze() method which can convert a mutable instance into an immutable one. Now if they'd just implicitly freeze() an instance whenever it's used as a key - that would work pretty well. As far as I can tell though, that doesn't actually happen. The freeze() method is a nifty idea that doesn't actually get used that much. [ July 14, 2005: Message edited by: Jim Yingst ]
"I'm not back." - Bill Harding, Twister
Joined: Feb 26, 2005
Originally posted by Jim Yingst: [EFH]: Plenty of mutable classes implement equals() and hashCode() based on their changeable state -- all of the Collections classes, for example.
Yes, but most of those classes don't come in two flavors - mutable and immutable - they way String and StringBuffer do. Given that many classes need to be mutable for some applications, but not for others, it would be a bit of a pain to make mutable and immutable versions of all our classes.
That's exactly what I faild to mean by inherently. THanks Jim for clarifying [ July 15, 2005: Message edited by: Bilal Sallakh ]