Java 对于插入后修改的实例,HashSet.contains(对象)返回false
根据所述方法,执行以下操作 如果此集合包含指定的元素,则返回true。更多 形式上,当且仅当该集合包含元素e时,返回true 这样(o==null?e==null:o.equals(e)) 但是,这似乎不适用于以下代码:Java 对于插入后修改的实例,HashSet.contains(对象)返回false,java,collections,hashset,Java,Collections,Hashset,根据所述方法,执行以下操作 如果此集合包含指定的元素,则返回true。更多 形式上,当且仅当该集合包含元素e时,返回true 这样(o==null?e==null:o.equals(e)) 但是,这似乎不适用于以下代码: public static void main(String[] args) { HashSet<DemoClass> set = new HashSet<DemoClass>(); DemoClass toInsert = new De
public static void main(String[] args) {
HashSet<DemoClass> set = new HashSet<DemoClass>();
DemoClass toInsert = new DemoClass();
toInsert.v1 = "test1";
toInsert.v2 = "test2";
set.add(toInsert);
toInsert.v1 = null;
DemoClass toCheck = new DemoClass();
toCheck.v1 = null;
toCheck.v2 = "test2";
System.out.println(set.contains(toCheck));
System.out.println(toCheck.equals(toInsert));
}
private static class DemoClass {
String v1;
String v2;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((v1 == null) ? 0 : v1.hashCode());
result = prime * result + ((v2 == null) ? 0 : v2.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DemoClass other = (DemoClass) obj;
if (v1 == null) {
if (other.v1 != null)
return false;
} else if (!v1.equals(other.v1))
return false;
if (v2 == null) {
if (other.v2 != null)
return false;
} else if (!v2.equals(other.v2))
return false;
return true;
}
}
publicstaticvoidmain(字符串[]args){
HashSet=newhashset();
DemoClass toInsert=新建DemoClass();
toInsert.v1=“test1”;
toInsert.v2=“test2”;
set.add(toInsert);
toInsert.v1=null;
DemoClass toCheck=新建DemoClass();
toCheck.v1=null;
toCheck.v2=“test2”;
System.out.println(set.contains(toCheck));
System.out.println(toCheck.equals(toInsert));
}
私有静态类DemoClass{
字符串v1;
字符串v2;
@凌驾
公共int hashCode(){
最终整数素数=31;
int结果=1;
result=prime*result+((v1==null)?0:v1.hashCode();
result=prime*result+((v2==null)?0:v2.hashCode();
返回结果;
}
@凌驾
公共布尔等于(对象obj){
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
DemoClass其他=(DemoClass)obj;
如果(v1==null){
如果(other.v1!=null)
返回false;
}else如果(!v1.equals(other.v1))
返回false;
如果(v2==null){
如果(other.v2!=null)
返回false;
}如果(!v2.equals(other.v2))
返回false;
返回true;
}
}
打印出:
假的
真的
因此,尽管equals
方法返回true
,HashSet.contains()
返回false
我猜这是因为我在将toInsert实例添加到集合后修改了它
然而,这是没有任何方式记录(或至少我无法找到这样的)。此外,还应使用上述equals方法引用的文档,但似乎并非如此。
HashSet
和HashMap
使用hashCode
和equals
方法在其内部结构中定位对象hashCode
用于查找正确的bucket,然后参考equals
来区分具有相同hashCode的不同对象,因为后者不能保证是唯一的。几乎在任何情况下,修改对象都是一个非常糟糕的主意,该对象在HashMap
中充当键,或者被放入HashSet
中。如果这些修改更改了hashCode或equals
方法的语义,将找不到您的对象。很明显,在添加到集合后,您正在将更改为insert.v1
,并且由于DemoClass
从v1
和v2
属性中获取hashCode,它找不到元素的已更改哈希代码。这是由设计行为决定的
HashSet
使用哈希来识别它所持有的对象
因此,如果在将对象放入集合后更改该对象,它可能无法找到该对象
您应该只保存不可变对象,或者只使对象的该部分可变,这不会影响哈希
我认为最好使用
HashMap
,它清楚地将可变部分和不可变部分分开。当一个对象存储在HashSet
中时,它将被放入一个数据结构中,该数据结构可以被对象的hashCode()
轻松(高效地)搜索。修改对象可能会更改其hashCode()
(取决于您如何实现它),但不会更新其在HashSet
中的位置,因为对象无法知道其包含在其中
你可以在这里做几件事:
hashCode()
的实现,使其不受所更改字段的影响。假设此字段对对象的状态很重要,并且参与了equals(object)
方法,这有点像代码味道,可能应该避免Set mySet=。。。;
DemoClass演示=。。。;
布尔值wasInSet=mySet.remove(演示);
demo.setV1(“新v1”);
demo.setV2(“新v2”);
如果(插图){
set.add(演示);
}
您更改了哈希值,哈希值被哈希集记住,因此无法识别对象。可能是的重复项
Set<DemoClass> mySet = ...;
DemoClass demo = ...;
boolean wasInSet = mySet.remove(demo);
demo.setV1("new v1");
demo.setV2("new v2");
if (wasInSet) {
set.add(demo);
}