【问题标题】:Hashing Equals only returns trueHashing Equals 只返回 true
【发布时间】:2015-01-30 10:59:30
【问题描述】:

考虑示例代码:

class EmployeeClass {
    int id;
    public EmployeeClass(int eid) {
        this.id=eid;
    }

    @Override
    public int hashCode(){
        return this.id;
    }
    @Override
    public boolean equals(Object o){
        return true;
    }
}

public class HashcodeAndEquals {

    public static void main(String[] args) {
        Map map=new HashMap();
        EmployeeClass e1=new EmployeeClass(1);
        map.put(e1, "Employee 1");                // line 1
        EmployeeClass e2=new EmployeeClass(2);
        map.put(e2, "Employee 2");
        EmployeeClass e3=new EmployeeClass(3);
        EmployeeClass e4=new EmployeeClass(1);   // line 2
        EmployeeClass e5=new EmployeeClass(1);
        map.put(e5, "Employee 5");                // line 3
        System.out.println("e1 -> "+map.get(e1)); 
        System.out.println("e2 -> "+map.get(e2));
        System.out.println("e3 -> "+map.get(e3));
        System.out.println("e4 -> "+map.get(e4)); // line 4
        System.out.println("e5 -> "+map.get(e5));
    }
}

输出:

e1 -> Employee 5
e2 -> Employee 2
e3 -> null
e4 -> Employee 5

e5 -> Employee 5

第 1 行运行后,第 3 行覆盖 e1 的值,但我的 equals 方法仅返回 true。同样在第 4 行,我们得到了 e4 的值,即使 equals 方法只返回 true。由于equals 方法没有比较只返回true,所以put 和get 在这里是如何工作的。幕后发生了什么?

【问题讨论】:

  • 你可以用调试器找出来吗?
  • 你对什么感到惊讶?键 e1、e4 和 e5 是等价的,因此它们指向相同的值。
  • 明白了。问之前一定用过。无论如何 gr8 帮助。

标签: collections map hashmap equals hashcode


【解决方案1】:

我猜您对并非所有人都是员工 5 感到惊讶? Equals 总是返回 true,所以 e5 应该已经覆盖了所有这些,对吧?

HashMap 在幕后使用“桶”。为了简单起见,假设只有 2 个。一个真正的 HashMap 有很多。

当你将一个对象放入 HashMap 时,它首先查看 hashCode。 (这就是为什么它被称为HashMap。)根据hashCode,它选择一个桶来存储它。对于e1,hashCode是1,所以它会选择桶1。对于e2,它是2,所以进入桶 2。e3 的 hashCode 是 3,但只有 2 个桶,所以它将进入桶 3 模 2,即 1。e4 出于同样的原因进入桶 2,e5 1. 回来了。

请注意,我在这里非常简化;实际上这个过程稍微复杂一些,但这应该足以解释事情了。

所以我们有以下桶:

bucket 1 | bucket 2
e5       | e4
e3       | e2
e1       |

所以现在是检索值的时候了。当我检索e1 时,hashCode 仍然是 1,所以它在存储桶 1 中查找。在那里,它选择第一个对象 equals e1,即 e5:你放入的最后一个对象是首先,显然。 e2 指向存储桶 2,因此在本示例中,您将得到 e4

在您的情况下,您实际上从未将 e3e4 放入 HashMap。但是因为e3 的hashCode 指向存储桶1,并且它等于e5,所以当您查找e3 时,您仍然会得到e5,即使它实际上并不存在。

正如我所说,这是对事物的极端简化。在真正的 HashMap 中,存储桶远不止 2 个。因此,在您的情况下,e3 有一个指向空存储桶的哈希码,而 e1e4e5 的哈希码显然都指向同一个存储桶。

这就是为什么在编写 hashCode 时给它一个良好的“分布”很重要的原因。如果你总是返回 42,那么所有对象总是会进入同一个存储桶,这会使你的 HashMap 变成一个非常尴尬的 ArrayList。您将失去 HashMap 为您带来的所有性能优势。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-18
    • 2021-01-26
    • 1970-01-01
    • 2011-12-29
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    相关资源
    最近更新 更多