This week's book giveaway is in the OCAJP 8 forum. We're giving away four copies of OCA Java SE 8 Programmer I Study Guide and have Edward Finegan & Robert Liguori on-line! See this thread for details.
What two statements are true about properly overridden hashCode() and equals() methods? A. hashCode() doesn’t have to be overridden if equals() is. B. equals() doesn’t have to be overridden if hashCode() is. C. hashCode() can always return the same value, regardless of the object that invoked it. D. If two different objects that are not meaningfully equivalent both invoke hashCode(), then hashCode() can’t return the same value for both invocations. E. equals() can be true even if it’s comparing different objects. Answers given are C, E. My question is regarding option E. Once the equals and hashCode are properly overrriden[This is mentioned in the question], how come two different objects can return true when equls() is called? Can any one give me an example?
Thomas, While your example is perfectly syntactical, do you think that class B's equal method is properly overriden? It is checking for instanceof A??? I agree that in perfect world if you call equals method on two diferent objects they might give true. But Hey we are living in ideal world when it comes to SCJP. equals() is overrridden perfectly. I like your example.
Howdy -- I'm just adding a bit here on how and why two different objects might be considered equal. Two different objects can be considered equal if YOU (the programmer of the class) decide that they should be. The purpose of .equals() is to allow a programmer to decide when and if two objects can be considered "meaningfully" equivalent. (And this becomes especially crucial when the object is being used as, say, a key in a HashMap or as an element in a HashSet). Remember, that == cares only if the bit patters in two different reference variables are identical. Which means, of course, that two different references are referring to the very same object on the heap. With .equals(), on the other hand, you might want to know that while two references refer to two different objects, those objects are considered "equal". The classic example (but with a gotcha of its own) is class String. If the user types in "Boulder" as the answer to a question, and you want to compare that to a list of cities in Colorado (perhaps in a String[ ] array), that would mean you have two different String objects that you want to test for equality: the "Boulder" that the user typed in (which you pulled from a JTextField) and the "Boulder" in your String[ ] of cities. If there were no .equals(), it would be MUCH harder to do that comparison! And as far as you're concerned, there is NO meaningful difference between "Boulder" entered by the user and "Boulder" in your String [ ] array. What makes the Strings a bad example, though, is that you can be fooled by the String constant pool into thinking that you can use == to find out if two different String objects are equal. That works in some situations (although not consistently across all VMs) because references to what you THINK are two different String objects can be redirected in such a way that both references point to the same String object in the String constant pool. But that is a special case that applies ONLY to String objects, and only because String objects are immutable and there is a constant pool for them. (Bottom line: don't rely on == to compare Strings!) So, what other situations might warrant two different objects being equal? What about the wrappers? Besides the static utility methods, a wrapper INSTANCE has only one purpose in life: to wrap a primitive value. There is no meaningful difference between an Integer object with the value of '3' and another Integer object with the value of '3'. As far as any code should be concerned, they're the same. (Although you can always use == if you really DO want to know that two different -- but equal -- objects are really two distinct objects.) That's why the .equals() method has been overridden in the wrapper classes, so that '2' and '2' are the same. (Although only when wrapped by the same class type. Two Integer instances with '2' will be considered equal but an Integer and a Long with '2' will NOT be considered equal. Java assumes that if you meant for a Long and an Integer to be equal, you wouldn't have used two different wrapper class types). Now imagine you use a class as a key into a HashMap. The key object has been put(key, object) into the HashMap. And now it comes time to get the object out by supplying the key -- you might get the key value from somewhere else, say, the user selected an ID number from a list or typed it in and you use their input to construct another instance of whatever your key class is. You've got two different objects now representing the key -- the one you originally used to PUT something in the HashMap, and the one you now have to use to GET the object. You want to know that if the key (let's say it's some kind of ID number) you used is 54678 (wrapped in an Integer), that if you make an Integer with that same number, the HashMap won't say, "I have never seen that key / number before in my life. Nope, there is no 54678 in THIS collection..." No, you want the HashMap to say to you, "Yes, I do have an object in here with a key that exactly matches the one you supplied, 54678, even though they are two different objects. Doesn't matter, your key tested positive for .equals() with a key I have in the collection." Most classes in Java do NOT have overridden .equals() methods, so most simply use == inside (that's what the inherited one from class Object does). But the wrappers and String class overrides .equals() to provide meaningful equivalence, and you can too. Just don't forget the contract -- if you have two objects that are considered equal, they MUST have the same hashcode as well, so be sure to do your equals() test on the same instance variables that you used to calculate your hashcode, in such a way that if these values change, and the hashcode values change, the equals() method will also reflect the change. So you don't have to use ALL instance variables to calculate your hashcode, just the ones that matter for equivalency. But you CAN have two different instances in a class return true for hashcode, yet still be different for .equals(). That simply means your hashcode algorithm may be less efficient than it could be. You could have a class, for example, where you override the hashcode method to: return 42; This means that all objects of that class will always have the same hashcode. This is legal and valid! Valid because it does not violate 'the contract'. If two objects are equal using .equals(), they will certainly have the same hashcode, and that's the contract. The contract does not say that if two objects are NOT equal they MUST have different hashcodes. The contract does NOT say that if two hashcodes are equal, the objects MUST be equal. The contract DOES say that if two hashcodes are not equal, the objects MUST NOT be equal. Should you override your .equals() method? If you ever care about meaningful equivalency, or if you ever want your object to be used in a collection that uses hashing, when two different objects might be used for putting and getting something out of the collection. Which means you will very likely, in the real world, want to override equals(), which means you must also override the hashcode method. cheers, Kathy (who believes that Ben and Jerry's chocolate fudge brownie will NEVER be meaningfully equivalent to any other flavor and/or brand of ice cream)
Joined: May 05, 2000
Originally posted by Kathy Sierra: The contract does not say that if two objects are NOT equal they MUST have different hashcodes.
And it's a good thing it doesn't! The hashCode method returns an int. But there are more possible Strings than there are ints. By the way, I added a hashCode method to my example for Bill!