场合
我需要覆盖equals()
,因为建议我也使用相同的字段覆盖hashCode()
方法。然后,当我看到一个只包含一个对象的集合时,我得到了令人沮丧的结果
set.contains(object)
=> false
,而
set.stream().findFirst().get().equals(object)
=> true
我现在明白了,这是因为object
添加到set
后再次更改其hashCode所做的更改。 contains
然后查看错误的密钥,找不到object
。
我对实施的要求是
equals()
Collections
或Maps
这样的灰HashSet
中安全地使用这些对象,即使它们容易发生变化。与
的惯例冲突equals()
和hashCode()
应使用相同的字段以避免意外(如此处所述:https://stackoverflow.com/a/22827702)。问题
仅使用equals()
中用于计算hashCode()
而非使用全部的字段的子集是否有任何危险?
更具体地说,这意味着:equals()
使用对象的多个字段,而hashCode()
仅使用 equals()
中使用的字段是不可变的。
我认为这应该没问题,因为
HashSet
中找到。相关帖子帮助我了解了我的问题,但没有解决方法:What issues should be considered when overriding equals and hashCode in Java?和Different fields for equals and hashcode
答案 0 :(得分:0)
合同确实会实现。合同规定.equal()
个对象始终相同.hashCode()
。相反的情况并非必须如此,我不知道有些人和IDE的痴迷恰恰适用于这种做法。如果所有可能的组合都可以,那么你会发现完美的哈希函数。
hashCode()
中的equals()
以及更少的字段中提供更多字段会违反合同。
答案 1 :(得分:0)
对于HashSet
和类似的集合/地图,让hashCode()
仅使用equals()
方法中的字段子集是一种有效的解决方案。当然,您必须考虑哈希码有效减少地图中的冲突。
但请注意,如果您想使用TreeSet
之类的有序集合,则会出现问题。然后你需要一个永远不会给“不同”对象发生冲突(返回零)的比较器,这意味着该集合只能包含一个碰撞元素。您的equals()
描述意味着将存在多个仅在可变字段中不同的对象,然后您将丢失:
所以我强烈建议再考虑你的对象类的平等和可变性概念。
答案 2 :(得分:0)
这对我来说完全有效。假设您有一个Person
:
final int name; // used in hashcode
int income; // name + income used in equals
name
决定参赛作品的位置(考虑HashMap
)或选择哪个参赛作品。
您将Person
作为Key
置于HashMap
内:根据hashcode
,它会转到某个存储桶,例如第二个。您升级income
并在地图中搜索Person
。根据{{1}},它必须在第二个桶中,但根据hashcode
,它不在那里:
equals
测试:
static class Person {
private final String name;
private int income;
public Person(String name) {
super();
this.name = name;
}
public int getIncome() {
return income;
}
public void setIncome(int income) {
this.income = income;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object other) {
Person right = (Person) other;
return getIncome() == right.getIncome() && getName().equals(right.getName());
}
}
你错过了我认为 HashSet<Person> set = new HashSet<>();
Person bob = new Person("bob");
bob.setIncome(100);
set.add(bob);
Person sameBob = new Person("bob");
sameBob.setIncome(200);
System.out.println(set.contains(sameBob)); // false
决定一个条目所在的桶(该桶中可能有许多条目)这是第一步,但是hashcode
决定是否很好,一个平等的进入。
您提供的示例完全合法;但是您链接的是另一种方式 - 它使用equals
中的更多字段,因此不正确。
如果您了解这些详细信息,则第一个哈希码用于了解可能驻留的位置,并且仅在以后所有这些(来自子集或存储桶)尝试通过hashcode
找到 - 你的例子是有意义的。
答案 3 :(得分:0)
hashCode()
可以使用equals()
使用的字段子集,但它可能会让您略微降低性能。
您的问题似乎是由于修改了对象,同时仍然在集合内部,以改变hashCode()
和/或equals()
的功能。每当您将对象添加到HashSet(或作为HashMap中的键)时,必须随后修改equals()
和/或{{1}使用的该对象的任何字段}}。理想情况下,hashCode()
使用的所有字段都应为equals()
。如果它们不可能,则必须将它们视为最终,而对象在集合中。
TreeSet / TreeMap也是如此,但适用于final
使用的字段。
如果您确实需要修改compareTo()
(或者在TreeSet / TreeMap的情况下由equals()
)使用的字段,您必须: