Java Map Implementation not based on HashCode - java

Is there some implementation of java.util.Map that does not uses HashCode?
I have the following problem:
I store an object associated to another object on a HashMap;
Change a property from the key object used on step 1;
As the hashcode is used to store the keys on the regular implementation of HashMap, when I perform a get() on the HashMap, I get null, because the old object hashCode was different at step 1.
Is there a solution for that? Or should I really use just immutable fields for my equals / hashCode methods?

IdentityHashMap uses the Object identity instead of the hashCode; however that does mean that you require the original object used as key to retrieve the value of the map. Other options would be redefine the hashcode to exclude the mutable parts of the object, or - if you can't redefine the hashCode for some reason - wrap the object in another object which provides a stable hashCode.

You would be well advised to use an immutable key, and to re-insert the key/value pair into Map, rather than mutating the key in-place. As you discovered, that just leads to weird bugs.
If this isn't an option for you, then see if you can ignore the mutable property in the hashCode() method, so that the hash code doesn't change. If that's the only property of the class, though, that's not a good idea.
You may be able to get away with using TreeMap, which I don't think uses hashCode(). However, it does require consistency between the key's compareTo() and equals() methods, so you may just end up with the same problem as before if the return values of those methods can change.

All Maps should use immutable objects for keys. True for Python; true for Java.
If you implement equals and hashCode using only immutable fields you should be fine.

How about removing and adding it again ?
On Step 2, You can remove the element added in Step 1 and again add it with new latest properties set. This way when you are try to get in Step 3, you will find it.
Try it.

I think modify the key object in map is not a good practice.
But if you really want, you can override the hashCode() and remember to override the equal() method.

All associative containers use comparing or hash code, so I would like to recommend you using immutable fields for equals() / hashCode() methods.

Override equals and hashCode methods if you don't want original implementation.

Related

why should I override equals and hashcode method for following scnerio

why I need to override for direct access of value in Hash map.That is if insert data into hashmap as follow HashMap,I could get value by giving the Key as Integer ,would get Object as Value.In this case is it necessary to Override equals() and hashCode() method?Please give suggestion.
No, you don't need to override anything to use an object as a value in a HashMap.
Only keys need to have a working hashCode().
However, you need to implement these two methods (technically only equals, but these two are a set, really) if you want to use things like Map#containsValue, List#indexOf or Collection#contains (and these should not just be using reference identity).
hashCode() is used to search for a specific elem when you want to retrieve it from a hashTable. hashCode() doesn't have to be distinct. in fact, you could just return the same integer for all your instance, but then, elems are stored in a list instead of a hashTable, and will cause a performance problem.
By default implementation of hashCode() (which is the implementation of Object for subClass to extents from )of JVM returns a integer according to the memory address of the object, so this should be enough, but this implement was not required by the JVM standard.
By default(Class object), implementation of equals() will return true and only return true when they have same reference , ie obj1 == obj2. read this
keep in mind that:
equal objects must have same hashCode()
those have same hashCode() are not required to be equal to each other.
I think override of hashCode() is not needed in most situations(not extends from other Class), cause modern JVMs has done
pretty good job for you.
So conclusion is:
if your super class have overwrite the hashCode() and equals() method, then you should override them, or at least take a look at the implementation, and decide whether you should override them.

How HashSet works with regards to hashCode()?

I'm trying to understand java.util.Collection and java.util.Map a little deeper but I have some doubts about HashSet funcionality:
In the documentation, it says: This class implements the Set interface, backed by a hash table (actually a HashMap instance). Ok, so I can see that a HashSet always has a Hashtable working in background. A hashtable is a struct that asks for a key and a value everytime you want to add a new element to it. Then, the value and the key are stored in a bucket based on the key hashCode. If the hashcodes of two keys are the same, they add both key values to the same bucket, using a linkedlist. Please, correct me if I said something wrong.
So, my question is: If a HashSet always has a Hashtable acting in background, then everytime we add a new element to the HashSet using HashSet.add() method, the HashSet should add it to its internal Hashtable. But, the Hashtable asks for a value and a key, so what key does it use? Does it just uses the value we're trying to add also as a key and then take its hashCode? Please, correct me if I said something wrong about HashSet implementation.
Another question that I have is: In general, what classes can use the hashCode() method of an java object? I'm asking this because, in the documentation, it says that everytime we override equals() method we need to override hashCode() method. Ok, it really makes sense, but my doubt is if it's just a recommendation we should do to keep everything 'nice and perfect' (putting in this way), or if it's really necessary, because maybe a lot of Java defaults classes will constantly uses hashCode() method of your objects. In my vision, I can't see other classes using this method instead of those classes related to Collections. Thank you very much guys
If you look at the actual javacode of HashSet you can see what it does:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
...
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
So the element you are adding is the Key in the backing hashmap with a dummy value as the value. this dummy value is never actually used by the hashSet.
Your second question regarding overriding equals and hashcode:
It is really necessary to always override both if you want to override either one. This is because the contract for hashCode says equal objects must have the same hashcode. the default implementation of hashcode will give different values for each instance.
Therefore, if you override equals() but not hashcode() This could happen
object1.equals(object2) //true
MySet.add(object1);
MySet.contains(object2); //false but should be true if we overrode hashcode()
Since contains will use hashcode to find the bucket to search in we might get a different bucket back and not find the equal object.
If you look at the source for HashSet (the source comes with the JDK and is very informative), you will see that it creates an object to use as the value:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
Each value that is added to the HashSet is used as a key to the backing HashMap with this PRESENT object as the value.
Regarding overriding equals() whenever you override hashCode() (and vice versa), it is very important that these two methods return consistent results. That is, they should agree with one another. For more details, see the book Effective Java by Josh Bloch.

Using HashMap with custom key

Quick Question: If I want to use HashMap with a custom class as the key, must I override the hashCode function? How will it work if I do not override that function?
If you don't override hashCode AND equals you will get the default behaviour which is that each object is different, regardless of its contents.
Technically, you don't have to override the hashCode method as long as equal objects have the same hashCode.
So, if you use the default behaviour defined by Object, where equals only returns true only for the same instance, then you don't have to override the hashCode method.
But if you don't override the equals and the hashCode methods, it means you have to make sure you're always using the same key instance.
E.g.:
MyKey key1_1 = new MyKey("key1");
myMap.put(key1_1,someValue); // OK
someValue = myMap.get(key1_1); // returns the correct value, since the same key instance has been used;
MyKey key1_2 = new MaKey("key1"); // different key instance
someValue = myMap.get(key1_2); // returns null, because key1_2 has a different hashCode than key1_1 and key1_1.equals(key1_2) == false
In practice you often have only one instance of the key, so technically you don't have to override the equals and hashCode methods.
But it's best practice to override the equals and hashCode methods for classes used as keys anyway, because sometime later you or another developer might forget that the same instance has to be used, which can lead to hard to track issues.
And note: even if you override the equals and hashCode methods, you must make sure you don't change the key object in a way that would change the result of the equals or the hashCode methods, otherwise the map won't find your value anymore. That's why it's recommended to use immutable objects as keys if possible.
The only time you don't have to override the hashCode() function is when you also don't override equals, so you use the default Object.equals definition of reference equality. This may or may not be what you want -- in particular, different objects will not be considered equal even if they have the same field values.
If you override equals but not hashCode, HashMap behavior will be undefined (read: it won't make any sense at all, and will be totally corrupted).
It depends on the object class you are using as a key. If it's a custom class like you propose, and it doesn't extend anything (i.e. it extends Object) then the hashCode function will be that of Object, and that will consider memory references, making two objects that look the same to you hash to different codes.
So yes, unless you are extending a class with a hashCode() function you know works for you, you need to implement your own. Also make sure to implement equals(): some classes like ArrayList will only use equals while others like HashMap will check on both hashCode() and equals().
Consider also that if your key is not immutable you may have problems. If you put an entry with a mutable key in the map an you change later the key in a way that it affects hashcode and equals you may lose your entry in the map,as you won't be able to retrieve it anymore.
You should override the equals() and hashCode() methods from the Object class. The default implementation of the equals() and hashcode(), which are inherited from the java.lang.Object uses an object instance’s memory location (e.g. MyObject#6c60f2ea). This can cause problems when two instances of the an objects have the same properties but the inherited equals() will return false because it uses the memory location, which is different for the two instances.
Also the toString() method can be overridden to provide a proper string representation of your object.
primary considerations when implementing a user defined key
If a class overrides equals(), it must override hashCode().
If 2 objects are equal, then their hashCode values must be equal as well.
If a field is not used in equals(), then it must not be used in hashCode().
If it is accessed often, hashCode() is a candidate for caching to enhance performance.

Setting own class as key in java Hashmap

I have a class which I want to set up as keys in HashMap. I already have implemented the compareTo method for that class. But still when I do:
map.put(new MyKey(dummyArguements) , dummyValue );
System.out.println(map.get( new MyKey(dummyArguements) ) );
I get null.
So that means hashmap is not able to identify that the two keys (for get & put call) are same.
Could someone help me here please ?
You need to implement hashCode() and equals(). compareTo() is additionally required for sorted map/set.
See this question for details.
You should implement equals() and hashCode(). Your class should also be immutable. If it is mutable, it's hash code can change after adding it to map. Then the map can have problems finding it.
1) In general for collections, what you want to override is the equals() method (and also the hashcode() method) for your class. compareTo()/Comparable and Comparator are typically used for sorting and only take the place of using the equals() method for object equivalance in some cases - examples are implementers of SortedSet such as TreeSet.
2) Please conform to Java naming standards in your code. Your class names should be capitalized... e.g new MyKey(dummyArguments). See http://www.oracle.com/technetwork/java/codeconventions-135099.html#367 (and http://www.oracle.com/technetwork/java/codeconvtoc-136057.html) for more detail.
HashMap doesn't check compareTo();
HashMap checks hashCode() and equals().
When using Collections that rely on hashing like Map and Set you have to implement the equals() and hashCode() to guarantee correct functionality. If you don't a new myKey will always be different from the key stored in the map because it uses the default implementations of equals() and hashCode().
Do you have the hashCode() defined? compareTo is needed for sorting.
As of java8 you should also implement Comparable (adding compareTo) because if the number of hash clashes exceeds 11, HashMap stores the entries in a binary tree. If you don't, performance suffers

object reference set in java

I need to create a Set of objects. The concern is I do not want to base the hashing or the equality on the objects' hashCode and equals implementation. Instead, I want the hash code and equality to be based only on each object's reference identity (i.e.: the value of the reference pointer).
I'm not sure how to do this in Java.
The reasoning behind this is my objects do not reliably implement equals or hashCode, and in this case reference identity is good enough.
I guess that java.util.IdentityHashMap is what you're looking for (note, there's no IdentityHashSet). Lookup the API documentation:
This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). (In normal Map implementations (like HashMap) two keys k1 and k2 are considered equal if and only if (k1==null ? k2==null : k1.equals(k2)).)
This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.
edit: See Joachim Sauer's comment below, it's really easy to make a Set based on a certain Map. You'd need to do something like this:
Set<E> mySet = Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
You could wrap your objects into a wrapper class which could then implement hashcode and equals based simply on the object's identity.
You can extend HashSet (or actually - AbstractSet) , and back it with IdentityHashMap which uses System.identityHashCode(object) instead of obj.hashCode().
You can simply google for IdentityHashSet, there are some implementations already. Or use Collections.newSetFromMap(..) as suggested by Joachim Sauer.
This of course should be done only if you are not in "possession" of your objects' classes. Otherwise just fix their hashCode()

Categories