有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

具有不正确的equals和HashCode实现的java HashMap

根据我读到的

to use an object as the key to a hashMap, it has to provide a correct override and implementation of the equals and hashCode method. HashMap get(Key k) method calls hashCode method on the key object and applies returned hashValue to its own static hash function to find a bucket location(backing array) where keys and values are stored in form of a nested class called Entry (Map.Entry). HashMap's internal hash Method defends against poor quality hash functions.

为了测试这些契约,我编写了一个bean类,其中包含了equalshashCode方法的不正确但合法的实现

班级:

public class HashVO {

    private String studentName;
    private int age;
    private boolean isAdult;

    public HashVO(String studentName, int age, boolean isAdult) {
        super();
        this.studentName = studentName;
        this.age = age;
        this.isAdult = isAdult;
    }
    public String getStudentName() {
        return studentName;
    }
    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public boolean isAdult() {
        return isAdult;
    }
    public void setAdult(boolean isAdult) {
        this.isAdult = isAdult;
    }
    @Override
    public String toString() {
        return studentName + " : " + age + " : " + isAdult;
    }
    @Override
    public boolean equals(Object obj) {
        return false;
    }
    @Override
    public int hashCode() {
        return 31;
    }

}

在本例中,HashMap的哈希方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

每次都应该返回相同的值,因为哈希代码总是返回31。因此,如果HashVO类的对象被用作hashMap的键,get方法不应该工作,因为它应该转到同一个bucket来检索对象,而equals方法总是返回false,因此它将无法找到键对象的匹配项

但当我使用这种方法时

public static void main(String[] args) {
        HashMap<HashVO, String> voMap = new HashMap<HashVO, String>();
        HashVO vo = new HashVO("Item1", 25, true);
        HashVO vo1 = new HashVO("Item2", 12, false);
        HashVO vo2 = new HashVO("Item3", 1, false);
        voMap.put(vo, "Item");
        voMap.put(vo1, "Item1");
        voMap.put(vo2, "Item2");
        System.out.println(voMap.get(vo));
        System.out.println(voMap.get(vo1));
        System.out.println(voMap.get(vo2));
    }

输出正确,并显示

Item
Item1
Item2

我想理解为什么在Equals和HashCode方法实现不正确的情况下,仍然会出现这种正确的输出


共 (2) 个答案

  1. # 1 楼答案

    HashMap的工作原理如下:

    1)表单元格的索引,其中(键,值)将计算保存为键。hashCode()

    2)HashMap中的键通过equals()或引用比较进行比较

    所以,在您的情况下,所有(K,V)对都将作为LinkedList存储在HashMap表的一个单元格中。 你们可以从地图上得到它们,因为对键的引用将等于

  2. # 2 楼答案

    HashMap有一个小技巧,它在使用equals之前比较对象引用。由于您使用相同的对象引用来添加元素和检索它们,HashMap将正确返回它们

    参见Java7源代码here(Java8对HashMap进行了相当大的修改,但它做了类似的事情)

    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }
    
        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            // HERE. Uses == with the key
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) 
                return e;
        }
        return null;
    }
    

    注意,这不是文档的一部分,所以不要依赖它