我们正在编写一个类,它需要非常复杂的逻辑来计算equals()和hashCode()。与...一致的东西:
Tuple<string, string>
我们不构造这些对象,它们是从外部复杂系统的XML反序列化的。有20多种类型,根据类型数据可以忽略,或用子进行处理,或者没有子进行处理,每种类型节点的数据比较取决于类型。
我们创建了equals()和hashCode()以反映所有这些规则,但最近遇到了一个问题,即hashCode与equals不同步,导致将相等的对象添加到HashSet两次。 我相信HashMap(以及HashSet)在Java中以这种方式实现:https://en.wikipedia.org/wiki/Hash_table实现首先将对象放在基于hashCode的桶中,然后对每个桶进行检查等于。在不幸的情况下,2个相等的对象将进入不同的桶,它们将永远不会被equals()进行比较。通过“不同步”这里我的意思是他们进入不同的桶。
确保equals和hashCode不会失去同步的最佳方法是什么?
修改: 这个问题与What issues should be considered when overriding equals and hashCode in Java?不同 他们在那里询问一般指导,并且接受的答案不适用于我的情况。他们说“make equals和hashCode一致”,在这里我要问我到底是怎么做的。
答案 0 :(得分:6)
Guava testlib库有一个名为EqualsTester
的类,可用于为equals()
和hashCode()
实现编写测试。
添加测试既可以帮助您确保代码现在正确,也可以确保在将来修改代码时保持正确。
答案 1 :(得分:5)
如果遍历算法足够复杂,您希望避免重复自己,请将算法隔离为equals
和hashCode
都可以使用的方法。
我看到两种选择,(在通常情况下)在广泛适用和有效之间进行权衡。
第一个选项是编写一个通用的遍历方法,它接受一个功能接口,并在遍历的每个阶段回调它,所以你可以传入一个lambda或实例,其中包含你想要执行的实际逻辑遍历; Visitor pattern。那个界面想要有办法说&#34;停止遍历&#34; (例如,当equals
知道答案是&#34;不等于&#34;)时,private boolean traverse(Visitor visitor) {
while (/*still traversing*/) {
if (!visitor.visitNode(thisNode)) {
return false;
}
/*determine next node to visit and whether done*/
}
return true;
}
可以保释。 从概念上来说,看起来像是:
equals
然后hashCode
和this
使用它来实现等式检查或哈希码构建,而不必知道遍历算法。
我在上面选择让方法返回一个标志,表示遍历是否提前结束,但这是一个设计细节。您可能不会返回任何内容,或者可能会返回equals
进行链接,无论您的情况如何。
然而,问题在于使用它意味着分配一个实例(或者使用lambda但是你可能需要为lamba分配一些内容来进行更新,以便跟踪它正在做什么)并做一个很多方法调用。在你的情况下,这可能很好;也许它是性能杀手,因为你的应用需要经常使用equals
。 : - )
...所以你可能想写一些特定于这种情况的东西,编写一些内置hashCode
和hashCode
逻辑的东西。它将在equals
使用时返回哈希码,或者private int equalsHashCodeWorker(Object other, boolean forEquals) {
int code = 0;
if (forEquals && other == null) {
// not equal
} else {
while (/*still traversing*/) {
/*update `code` depending on the results for this node*/
}
}
return code;
}
的标志值(0 =不等于,!0 =相等)。不再普遍有用,但它避免了创建一个访问者实例传入/ lambda开销/调用开销。 概念,这可能类似于:
other
具体而言,具体针对您的案例以及您的风格指南等。有些人会让equals
参数有两个目的(标志和&#34;其他&#34;对象),让other == null
处理null
个案本身并且只调用这个工作者当它有一个非hashCode
对象时。我宁愿避免加倍这样的论点的含义,但你经常看到它。
无论你走哪条路,如果你在一家拥有测试文化的商店里,你自然会想要为你已经看过的复杂案例以及其他案例进行测试。你看到失败的机会。
hashCode
无论如何,如果您希望调用hashCode
很多,您可以考虑将结果缓存到实例字段。如果您正在使用的对象是可变的(并且听起来像是这样),那么只要您改变对象的状态,就会使存储的哈希码无效。这样,如果对象没有更改,则不必在后续调用c=0
x=[]
y=[]
z=[]
"""o=int(input("enter menu:"))"""
while True:
print("o=1:::add ","o=2:::max ","o=3:::find ","o=4:::remove ","o=5:::report ","o=6:::exit")
o=int(input("enter menu:"))
if o==1:
x.append(input("enter name:"))
z.append(int(input("enter #:")))
y.append(int(input("enter score:")))
c+=1
elif o==2:
i=1
for i in range(len(x)):
y[i]= y[i] if y[i]>y[i-1] else y[i-1]
"""i+=1"""
f=y[i-1]
print("max=",y[i-1])
elif o==3:
n=int(input("enter number:"))
for i in range(len(x)):
if n==z[i]:
print(x[i],y[i])
else:
print("not found")
elif o==4:
p=int(input("enter student number:"))
for i in range(len(x)):
if p==z[i]:
x.pop(i)
y.pop(i)
z.pop(i)
elif o==5:
for i in range(len(x)):
print("name:",x[i],"st number:",z[i],"score:",y[i])
elif o==6:
break
时重复遍历。但是,当然,如果你忘记在mutator方法的一个中使哈希码无效......
答案 2 :(得分:5)
condsider的一个选项可能是代码生成。基本上,您写出了需要比较的事物列表,并且有一个生成equals方法和hashcode方法的程序。由于两种方法都是从相同的事物列表中生成的,因此它们不应该不同步(前提是各个元素不一致)。
答案 3 :(得分:0)
如果a.equals(b)
,则表示a.hashcode() == b.hashcode()
。
但是,要小心。 !a.equals(b)
不暗示a.hashcode() != b.hashcode()
。
这主要是因为哈希冲突可能是一个严重的问题,具体取决于您的算法和大量因素。通常,如果两个对象相等,则其哈希码始终相等。但是,您无法仅通过比较哈希码来确定两个对象是否相等,因为a.hashode() == b.hashcode()
也不暗示a.equals(b)
。