So basically if i have the following class
I want to be able to compare instances of this class based on the state of storage. It works , but i have a warning that i am not changing hashCode(). Ok, but as i look into the contract for hashCode() , it says it should not be changing throughout the life of the instance and if equals returns true, this means that the two instances have the same hashCode as well. How can i achieve this in a scenario when the variables used in equals are potentially changing?
Is manually adding a specific attribute to serve this purpose only( and additionally be used in HashMaps and whatnot, if needed, as it will not be identical between all instances) a good idea? Like this
Is the cast to the raw variant of CustomClass ok ? It removes the warnings that i get if i cast using CustomClass<E> and serves the same purpose. If its not , why, and is there an alternative for casting the Object input ?.
Claude Sylvanshine wrote:as i look into the contract for hashCode() , it says it should not be changing throughout the life of the instance
I see nothing in the documentation that says that. Can you give us a reference to where you are getting this information? Or maybe you simply misunderstood this part:
Java API for Object.hashCode() wrote: the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.
That doesn't say what you seem to think it says. All that means is that as long as there are no changes to the properties on which equals() comparisons are made, hashCode() should return the same integer.
hashCode() should be calculated dynamically if it's based on mutable (can change) fields. So, whenever a mutable field of an object is changed, its hashCode should potentially also change. I say potentially because two objects that are NOT equal() can actually have the same hashCode.
The only time it's safe to use a static, once-only calculated hash code is when it is based on immutable properties of the object and equals() is based on the exact same set of properties as well.
The official JavaDoc contract for hashCode() is harder to read than it needs to be. The
three points in the contract boil down to these:
■ Within the same program, the result of hashCode() must not change. This means that
you shouldn’t include variables that change in figuring out the hash code. In our hippo
example, including the name is fine. Including the weight is not because hippos change
■ If equals() returns true when called with two objects, calling hashCode() on each of
those objects must return the same result. This means hashCode() can use a subset of
the variables that equals() uses. You saw this in the card example. We used only one
of the variables to determine the hash code.
■ If equals() returns false when called with two objects, calling hashCode() on each of
those objects does not have to return a different result. This means hashCode() results
do not need to be unique when called on unequal objects
Its not stated as mandatory obviously, but i assumed it is pretty much expected to behave like that. Its a fact that your quote from the API is not as strict as this . Or is my English failing me somehow?
First of all: if two objects are equal then they must have the same hashcode. That's the only "must" you need to look at for equals and hashcode. If two objects aren't equal then there is no rule about their hashcodes.
So, let's suppose you have two objects which are not equal. Then it's possible (maybe even likely) that they have different hashcodes. Let's suppose for now that the hashcodes are different.
Now suppose you change some mutable fields in the objects and the result is that they are now equal. This means that their hashcodes must now be equal, right? But before we changed those fields their hashcodes were different. So it follows that changing the mutable fields should result in changing the hashcodes so that the one and only "must" is obeyed.
Now you've seen people (and quotes) in this thread which says that hashcodes must not change. That's a slightly different kind of "must". Here's the reason for it: Let's suppose you add an object to a Map and the object's hashcode is, say, 2371. And then let's suppose something causes the object's hashcode to 447. Then looking for the object in the Map is likely to fail because the Map put it in there with hashcode 2371 and will look for it based on that. And that is a Bad Thing™.
Moral of the story: Having two objects for which the equals() method sometimes returns true and other times false, in the same run of an application, can cause problems if you need to put those objects into a Map. If you're not going to put them into a Map then the hashcode value becomes irrelevant. But if you're going to do that weird thing with the equals() method you better be sure that you plan to never put the objects into a Map.
@ Paul - thanks a lot for the explanation. I decided to look at how obviously mutable classes handle this and checked the code for AbstractList ones, that the other list collections get. Well the hashCode is changing in parallel with the contents of the list. So i tried to put one in a map , just to see for myslf and yes, it messes it up if we modify it after adding it.
Here is the code.
Ok its something i will learn to live with i guess. Thanks again everyone.
Piet Souris wrote:To promise never to use your objects in a Map, well, thát would not be very reliable, me thinks.
No, the word for that development technique is "ticking time bomb".
Claude Sylvanshine wrote:Ok its something i will learn to live with i guess...
However, it ONLY applies to HashMaps (or any other "hashed" collection). In theory, there's no reason why you shouldn't be able to change the keys of a TreeMap ... although whether you actually can in practise is another matter.
I think the idea with hash code methods and the List interface is that all Lists containing the same contents will return the same hash code. Check the List#equals() method, too. You find that two different List implementations but with the same contents will return true from equals. They must therefore return the same value from hashCode, which is why ArrayList inherits its equals and hash code methods from AbstractList. So does LinkedList, for the same reason.
Claude Sylvanshine wrote:. . . checked the code for AbstractList ones, that the other list collections get. . . . something i will learn to live with i guess. Thanks again everyone.
You will find that Maps always have problems if you use mutable objects as keys: have a look at this:-
...from the Map interface documentation. That means that you cannot usually find the key in the Map. The same applies to hash‑based Sets.
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
Winston Gutkowski wrote:However, it ONLY applies to HashMaps (or any other "hashed" collection). In theory, there's no reason why you shouldn't be able to change the keys of a TreeMap ... although whether you actually can in practise is another matter.
Right, that's an issue which I glossed over. And yes, a TreeMap uses the compareTo() method of its entries rather than the hashCode() method. However if you have objects whose equals() method isn't stable, then chances are the compareTo() method isn't stable either, and then perhaps a TreeMap would have problems dealing with such objects anyway. But I haven't written the "smoking gun" test program like the one Claude wrote earlier in this thread, for that situation.
I checked the listed methods , but my main goal was to see if they are having some magic that will make them have constant hashCode and variable equals results. Which, right now, i cant think of a case where it can be actually useful. The need for consistency based on this rule has way more substance to it , for me personally, after this discussion.
If equals() returns true when called with two objects, calling hashCode() on each of
those objects must return the same result.