覆盖Java中的hashCode()和equals()方法
问题描述
正如标题所示,我的问题与覆盖hashCode()和equals()方法有关。但是,这也不是完全正确的,我只是不知道另一种方式来总结我的问题。
我有一个包含多个组件的对象Label
,其中一个是List
,其中包含多个Node
类型的对象。 Label
的示例为:[(n1, n2, n3), (n4, n5)]
。我想存储在Label
中生成的所有唯一LinkedHashSet
对象。但是,这不能按预期方式工作。假设LinkedHashSet
当前包含上述Label
,并且我们现在生成了一个名为Label
的新other
,结果它包含与已添加标签相同的节点,因此也包含[(n1, n2, n3), (n4, n5)]
。由于它具有相同的节点列表,因此Label
中的其他组件也相同。我不会在这里解释原因,只是假设是这样,因为是这种情况。但是,在检查LinkedHashSet
是否已包含Label
时,由于对象具有不同的对象ID,因此它将返回false
。
一种方法是在LinkedHashSet
上编写一个for循环,然后将新标签与LinkedHashSet
中的所有标签进行比较,但这在运行时间方面将非常昂贵,因此我希望找到一种更便宜的标签选项。欢迎任何建议!
另一种方法是采用equals()
方法,但是没有用,因为我还必须采用hashCode()
方法,并且我不知道要进行什么更改才能使它起作用。
思路一:
实现equals(...)和hashCode(...)是正确的方法。通常人们使用他们的IDE为他们生成这些方法,这就是Java 7+的样子:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Label label = (Label) o;
return Objects.equal(nodes, label.nodes);
}
@Override
public int hashCode() {
return Objects.hashCode(nodes);
}
如果您有多个字段,则变为:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Label label = (Label) o;
return Objects.equal(nodes, label.nodes) &&
Objects.equal(other, label.other);
}
@Override
public int hashCode() {
return Objects.hashCode(nodes, other);
}
但是,只有在Node
的实现还实现equals(...)
和hashCode(...)
的情况下,这才起作用。
思路二:
在@EqualsAndHashCode
(或lombok
)类别中使用Node
中的Label
。
然后您可以将节点添加到LinkedHashSet
。
思路三:
您绝对不希望“循环LinkedHashSet
并进行比较...”。检测重复是LinkedHashSet
应该做的。为此,它需要equals
和hashCode
的适当实现。
[您可能知道您需要实现equals
,以便当Label
等于另一个Label
时它返回true,但是您对此进行了定义。如果两个标签具有相同的节点时相等,那么是的,您几乎必须查看所有节点并检查它们是否相同。
留下hashCode
。您必须实现hashCode
与equals
一致,即,如果两个标签相等,则它们必须具有相同的哈希码。这是因为LinkedHashSet
将使用哈希码来确定Label
所在的存储桶,然后使用equals
将新的Label
与存储桶中已经存在的存储桶进行比较。如果两个标签相等但生成不同的哈希码,则LinkedHashSet
将无法将它们检测为重复。
最简单的方法是将所有节点的哈希码合并到Label
的哈希码中。类似于:
int hashCode() {
int hc = 1;
for (Node n : allMyNodes) {
hc = hc * 31 + n.hashCode();
}
return hc;
}
[如果有很多节点,并且除非“相等”,否则两个标签不太可能共享同一节点,您可以只使用第一个节点的哈希码,而不是遍历它们。”>