我创建了一个名为Coordinates
的类,它只包含一些x
和y
个整数。我想将其用作HashMap
的密钥。
但是,我注意到当您创建具有相同Coordinates
和x
值的y
的两个不同实例时,它们将被哈希映射用作不同的键。也就是说,即使两个条目具有相同的坐标,也可以输入两个条目。
我已覆盖equals()
:
public boolean equals(Object obj) {
if (!(obj instanceof Coord)) {
return false;
}else if (obj == this) {
return true;
}
Coord other = (Coord)obj;
return (x == other.x && y == other.y);
}
但是HashMap
仍然使用这两个实例,就像它们是不同的键一样。我该怎么办?
我知道我可以使用两个元素的整数数组。但我想用这个课。
答案 0 :(得分:6)
您需要覆盖hashCode
。 Java 7为此提供了一种实用方法。
@Override
public int hashCode() {
return Objects.hash(x, y);
}
答案 1 :(得分:4)
您还应该覆盖hashCode()
,以便两个相等的实例具有相同的hashCode()
。 E.g:
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
请注意,对于两个不等于具有不同哈希码的实例,并不是严格要求,但是您拥有的冲突越少,您从HashMap
获得的性能就越好。
答案 2 :(得分:1)
哈希映射使用hashCode
对象方法来确定将对象放入哪个存储桶。
如果您的对象未实现hashCode
,则它会继承Object
的默认实现。来自docs:
尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)
因此,每个对象看起来都是不同的。
请注意,不同的对象可能会返回相同的hashCode
。
这称为碰撞。
当发生这种情况时
然后除hashCode
之外,
哈希映射实现将使用equals
方法来确定两个对象是否相等。
请注意,大多数IDE都提供从您的类中定义的字段生成equals
和hashCode
方法。事实上,IntelliJ鼓励同时定义这两种方法。有充分的理由。这两种方法密切相关,
每当你更改其中一个,或实现其中一个,或覆盖其中一个,
你必须审查(并且最有可能改变)另一个。
此类中的方法是100%生成的代码(通过IntelliJ):
class Coord {
private int x;
private int y;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coord coord = (Coord) o;
if (x != coord.x) return false;
if (y != coord.y) return false;
return true;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
答案 3 :(得分:0)
您可能没有覆盖hashCode
方法。为什么需要这个?要回答这个问题,您必须了解散列表的工作原理。
哈希表基本上是一个链表列表。数组中的每个桶对应于hashCode % numberOfBuckets
的特定值。具有相同hashCode % numberOfBuckets
的所有对象将存储在关联存储桶中的链接列表中,并且将基于其equals
方法识别(在查找期间)。因此,确切的规范是a.hashCode() != b.hashCode() => !a.equals(b)
,相当于a.equals(b) => a.hashCode() == b.hashCode()
。
如果使用基于引用的hashCode
的默认实现,那么两个相等但具有不同引用的对象(因此,很可能是不同的hashCode)将存储在不同的桶,导致重复的密钥。