Updating Java HashMap key

有些话、适合烂在心里 提交于 2019-11-30 18:33:11

The javadoc explains it

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.

Basically, don't use mutable objects as keys in a Map, you're going to get burnt

To extrapolate, because the docs may not appear clear, I believe the pertinent point here is `changed in a manner that affects equals', and you seem to be assuming that equals(Object) is called each time contains is invoked. The docs don't say that, the wording implies they may be allowed to cache computations.

Looking at the source, it seems that because your hashCode returns a different value (was 5, now 6), it's possible that it's being looked up in a different bucket based on implementation details.

You can think of if this way, the Map has 16 buckets. When you give it an object with A == 5, it tosses it into bucket 5. Now you can change A to 6, but it's still in bucket 5. The Map doesn't know you changed A, it doesn't rearrange things internally.

Now you come over with another object with A == 6, and you ask the Map if it has one of those. It goes and looks in bucket 6 and says "Nope, nothing there." It's not going to go and check all the other buckets for you.

Obviously how things get put into buckets is more complicated than that, but that's how it works at the core.

The HashMap puts your object at the location for hash key 5. Then you change the key to 6 and use containsKey to ask the map whether it contains the object. The map looks at position 6 and finds nothing, so it answers false.

So don't do that, then.

When you put "m1" the first time around, hashCode() was 5. Thus the HashMap used 5 to place the value into the appropriate bucket. After changing m2, the hashCode() was 6 so when you tried looking for the value you put in, it the bucket it looked in was different.

Jingguo Yao

A code example to accompany ptomli's answer.

import java.util.*;

class Elem {
    private int n;

    public Elem(int n) {
        this.n = n;
    }

    public void setN(int n) {
        this.n = n;
    }

    @Override
    public int hashCode() {
        return n;
    }

    @Override
    public boolean equals(Object e) {
        if (this == e)
            return true;
        if (!(e instanceof Elem))
            return false;
        Elem an = (Elem) e;
        return n == an.n;
    }
}

public class MapTest {
    public static void main (String [] args)  {
        Elem e1 = new Elem(1);
        Elem e2 = new Elem(2);

        HashMap map = new HashMap();
        map.put(e1, 100);
        map.put(e2, 200);

        System.out.println("before modification: " + map.get(e1));  
        e1.setN(9);
        System.out.println("after modification using updated key: " + map.get(e1)); 

        Elem e3 = new Elem(1);
        System.out.println("after modification using key which equals to the original key: " + map.get(e3));    
    }
}

Compiles and runs it. The result is:

before modification: 100
after modification using updated key: null
after modification using key which equals to the original key: null

I am using Java 6 on Linux.

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