作者:大帅哥石头2011 | 来源:互联网 | 2023-01-03 10:43
考虑员工类 -
public class Employer implements Serializable{
private Long id;
private String name;
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj instanceof Employer) {
Employer employer = (Employer) obj;
if (this.id == employer.id) {
return true;
}
}
return false;
}
//Idea from effective Java : Item 9
@Override
public int hashCode() {
int result = 17;
result = 31 * result + id.hashCode();
//result = 31 * result + name.hashCode();
return result;
}
}
创建了2个员工对象 -
Employer employer1 = new Employer();
employer1.setId(10L);
Employer employer2 = new Employer();
employer2.setId(11L);
将它们添加到hashset后,大小将为2. HashSet内部使用hashmap来保持唯一性 -
private transient HashMap map;
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
现在,如果我将第二个员工的id设置为与第一个员工的id相同,即 -
employer2.setId(10L);
大小仍然是2.为什么不是1?in-variants是否会被破坏?
1> Eran..:
大小仍然是2.为什么不是1?in-variants是否会被破坏?
如果修改用于计算的任何属性hashCode
以及equals
已在其中的实例HashSet
,则HashSet
实现不会知道该更改.
因此它将保留这两个实例,即使它们现在彼此相等.
您不应对作为成员或HashSet
s(或HashMap
s中的键)的实例进行此类更新.如果您必须进行此类更改,请从变更Set
之前删除该实例,然后再重新添加.
2> dasblinkenli..:
所有基于散列的容器(包括HashSet
)都对其键的哈希代码做了一个非常重要的假设:它们假设哈希代码在对象位于容器内时永远不会改变.
您的代码通过在实例仍在哈希集中时修改实例来违反此假设.没有切实可行的方法HashSet
来应对此更改,因此您必须选择以下两种方法之一来处理此问题:
永远不要修改基于散列的容器的密钥 - 这是迄今为止最常用的方法,通常通过使哈希密钥不可变来实现.
跟踪修改,并手动重新哈希对象 - 本质上,您的代码确保在哈希键位于容器外时对哈希键进行所有修改:从容器中删除对象,进行修改,然后将其放回原位.
第二种方法经常成为维护头痛的根源.当您需要在基于散列的容器中保留可变数据时,一种好的方法是final
在计算哈希代码和相等性检查时仅使用字段.在您的示例中,这将意味着创建id
字段final
,并setId
从类中删除方法.