需要帮助来了解HashMap的行为

时间:2015-08-10 15:10:22

标签: java hashmap equals

假设我有一个人类,并且相等性基于id属性。以下是Person class -

的实现
class Person {

    private int id;
    private String firstName;

    public Person(int id, String firstName) {
        super();
        this.id = id;
        this.firstName = firstName;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public int hashCode() {
        return this.id;
    }

    public boolean equals(Object obj) {
        return ((Person) obj).getId() == this.id;
    }
}

我使用Person类作为HashMap的键。现在看下面的代码 -

import java.util.HashMap;

public class TestReport {
    public static void main(String[] args) {

        Person person1 = new Person(1, "Person 1");
        Person person2 = new Person(2, "Person 2");

        HashMap<Person, String> testMap = new HashMap<Person, String>();
        testMap.put(person1, "Person 1");
        testMap.put(person2, "Person 2");

        person1.setId(2);

        System.out.println(testMap.get(person1));
        System.out.println(testMap.get(person2));

    }
}

注意,虽然我们已经添加了两个不同的person对象作为HashMap的键,但后来我们将id person1对象更改为2以使person对象相等。

现在,我的输出为 -

Person 2
Person 2

我可以看到HashMap中有两个键值对,其中包含数据:“person1 / Person 1”和“person2 / Person 2”,我仍然总是将“Person 2”作为输出,我永远无法访问值“人1”。另请注意,我们在HashMap中有重复键。

在查看源代码后我可以理解这种行为,但它似乎不是问题吗?我们可以采取一些预防措施来阻止它吗?

2 个答案:

答案 0 :(得分:1)

这种情况正在发生,因为您的equals()方法仅使用哈希码。您应该比较所有人员字段,例如firstName

答案 1 :(得分:1)

这完全取决于hashCode()使用HashMap值的方式。

虽然需要两个相同哈希码的相等对象,但反向不一定正确。两个不相等的对象可以具有相同的哈希码(因为int只有有限的可能值集)。

每次将对象放入HashMap时,它都会将对象存储在由密钥hashCode()标识的存储桶中。因此,根据hashCode()的实现方式,您应该在各种存储桶中公平分配条目。

现在,当您尝试检索值时,HashMap将识别给定key所属的存储桶,然后将遍历该存储桶中的所有密钥以选择给定密钥的条目 - 在此阶段,它将使用equals()方法来识别条目。

在您的情况下,person1位于bucket 1person2位于bucket 2

但是,当您通过更新hashCode()更改了person1的{​​{1}}值时,id并未意识到此更改。稍后,当您使用HashMap作为密钥查找条目时,person1认为它应该出现在HashMap中(因为bucket 2现在是person1.hashCode()) ,之后当它使用2方法迭代bucket 2时,它会找到equals的条目,并认为它是您感兴趣的对象person2 case也基于equals属性。

如果查看implementation of HashMap#get方法,上面的说明很明显,如下所示:

id

PS 有时当您知道问题的答案时,您忘记查找重复的问题,并在任何人都可以回复之前直接回答问题 - 这就是本案中发生的事情。下次要小心: - )