【发布时间】:2018-02-08 12:33:31
【问题描述】:
如果 hashCode() 计算 使用不可变字段 并且 equals() 使用所有字段,当类用作哈希键时会不会有问题?例如
import java.util.Objects;
public class Car {
protected final long vin;
protected String state;
protected String plateNumber;
public Car( long v, String s, String p ) {
vin = v; state = s; plateNumber = p;
}
public void move( String s, String p ) {
state = s; plateNumber = p;
}
public int hashCode() {
return (int)( vin % Integer.MAX_VALUE );
}
public boolean equals( Object other ) {
if (this == other) return true;
else if (!(other instanceof Car)) return false;
Car otherCar = (Car) other;
return vin == otherCar.vin
&& Objects.equals( state, otherCar.state )
&& Objects.equals( plateNumber, otherCar.plateNumber );
}
}
在汽车对象插入哈希集后,它会在汽车对象上调用 move(),这可能通过保存在别处的引用来实现。
我不关心这里的性能问题。只有正确性。
我已阅读 java hashCode contact,关于 SO 的答案很少,包括尊敬的 Jon Skeet 的 this 和来自 big blue 的 this。我觉得最后一个链接给出了最好的解释,暗示上面的代码是正确的。
编辑
结论:
此类满足 java 中对“equals()”和“hashCode()”的约束。然而,当用作集合中的键时,它违反了对“equals()”的附加要求,无论是否散列。
附加要求是,只要对象是键,'equals()' 需要保持一致。
请参阅下面 Louis Wasserman 的反例和 Douglas 提供的参考资料。
一些澄清:
A) 这个类满足 java 对象级别的约束:
- (carA == carB)暗示(carA.hashCode() == carB.hashCode())
- ( carA.hashCode() != carB.hashCode() ) 暗示 (carA != carB )
- equals() 需要是自反的、对称的、传递的。
- hashCode() 需要保持一致。即在其生命周期内无法更改对象。
- equals() 需要保持一致只要两个对象都没有被修改。
请注意,“1.”和“2.”的倒数不是必需的。并且上面的类满足所有条件。
java 文档还提到 "equals() ... 在对象上实现了最有区别的可能等价关系",但不确定这是否是强制性的。
B) 至于性能,碰撞避免概率的增量随着我们组合的每个连续成员变量而降低。通常选择几个精心挑选的成员变量就足够了。
【问题讨论】:
-
如果equals和hashcode不一致,那么问题就更大了