when add key-value pair to a hashMap, why Java makes hashMap's hashCode change?

♀尐吖头ヾ 提交于 2019-12-23 14:51:14

问题


If you look at java's hashCode method inside hashMap, you will find:

public int hashCode() {
    int h = 0;
    Iterator<Entry<K,V>> i = entrySet().iterator();
    while (i.hasNext())
        h += i.next().hashCode();
    return h;
}

So when you insert things into hashMap, hashMap's hashCode will change. Thus, if we insert an empty hashMap into hashSet, then insert something to this hashMap, then call hashSet.contains(hashMap), it will return false. Why does Java allow such behavior? This will easily cause duplicate items in a hashSet.

Try run the following code:

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

public class Main {

    public static void main(String[] args) {

        HashSet<HashMap<Integer, String>> set = new HashSet<>();
        HashMap<Integer, String> b = new HashMap<>();
        System.out.println("adding hashcode: " + b.hashCode() + "to set");
        set.add(b);
        b.put(8, "arsenal");
        for(HashMap<Integer, String> map: set){
            Iterator it = map.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pair = (Map.Entry)it.next();
                System.out.println(pair.getKey() + " = " + pair.getValue());
            }
        }
        System.out.println("Finding b: " + set.contains(b));
        System.out.println(b.hashCode());

        set.add(b);

        for(HashMap<Integer, String> map: set){
            Iterator it = map.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pair = (Map.Entry)it.next();
                System.out.println(pair.getKey() + " = " + pair.getValue());
            }
        }

    }
}

回答1:


Why Java allow such behavior?

Because keys are supposed to be invariant, according to their equals() implementation. If you mutate a key in such a way that comparisons by means of its equals() method are affected, then the behaviour of the map is unspecified.

And this is exactly what you're doing when you change your HashMap while it's an element of the HashSet, since HashSet is actually backed by a HashMap.

This is an excerpt of the Map interface documentation:

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. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.

If I were you, I wouldn't use a mutable map as the key of a structure that relies on the immutability of its elements.




回答2:


The Pigeonhole principle gaurantees there may be collisions. There are other Map implementations, including LinkedHashMap and TreeMap which do not exhibit the behavior you describe. In the case of LinkedHashMap it is described in the Javadoc (in part) as a hash table and linked list implementation of the Map interface, with predictable iteration order.



来源:https://stackoverflow.com/questions/32556185/when-add-key-value-pair-to-a-hashmap-why-java-makes-hashmaps-hashcode-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!