哈希码或哈希图是如何工作的,如果我们重写总是返回常量的哈希码,而覆盖的equals方法则返回false,那么它如何在返回或删除时识别出确切的对象? time bean忘记了所有这些性能,我的问题是它如何识别确切的对象,让我进一步解释,我有一个包含两个字段的人员类,并且覆盖了返回始终为1的哈希码和覆盖了返回false的equals方法,创建了3个对象,对象1-id 10名称AAAA,对象2-id 20,名称BBB,对象3-id 30,名称CCC,我将所有三个对象添加到hashSet中,然后删除了对象2,这里是如何识别确切的对象(20,BBB)
答案 0 :(得分:2)
使用HashMap
/ HashSet
或其他使用它来优化比较/搜索的代码时,常量哈希码是有效的,并且“仅”是性能问题。
一个始终返回equals()
的{{1}}实现会破坏平等契约,并会导致许多类型的集合出现问题/令人惊讶的行为。
equals方法对非null对象引用实现等效关系:
- 这是自反的:对于任何非空参考值x,x.equals(x)应该返回true。
- 这是对称的:对于x和y的任何非空参考值,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
- 是可传递的:对于x,y和z的任何非空引用值,如果x.equals(y)返回true,而y.equals(z)返回true,则x.equals(z)应该返回true 。
- 这是一致的:对于任何非空参考值x和y,只要未修改对象的equals比较中使用的信息,对x.equals(y)的多次调用将始终返回true或始终返回false。
- 对于任何非空参考值x,x.equals(null)应该返回false。
false
实现违反了第一个要求。
- 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
正如@Mensur Qulami在评论中指出的那样,如果您的实现使用与return false
的引用比较来优化对节点的搜索,它似乎仍然可以与HashMap
一起正常工作。
来自OpenJDK 12 HashMap.getNode(int hash, Object key):
==
因此,此实现在尝试((k = first.key) == key || (key != null && key.equals(k))))
之前检查引用是否相等,但这不能保证。
JavaDocs for HashMap.get
严格按照equals()
的定义
更正式地说,如果此映射包含从键k到值v的映射,使得
equals()
,则此方法返回(key==null ? k==null : key.equals(k))
;否则返回v
。
(这等效于 if null
的实现满足上述约定,因此OpenJDK进行的优化有效)
答案 1 :(得分:1)
即使equals
返回false,它仍然可以删除对象,因为==
仍然返回true(==
比较实际的对象引用,不受{{ 1}}实施)。
HashSet remove最终调用HAshMap,removeNode包含此比较以查看是否找到该值。
equals
它比较hashCode,因为它是一个常量,所以始终相等,然后首先使用 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
进行比较。如果密钥不是同一对象(即==
为假),则仅使用==
来检查它们是否相等。
如果您要创建一个具有相同值的新对象,则永远无法使用它从集合中删除该值,但是当您使用同一对象时,由于equals
仍然可以使用