Java 对于有效的散列键,overrided equals不是必需的
在我看到的所有关于重写equals和hashcode方法的问题中,人们总是说,如果重写equals方法,那么也应该重写hashcode方法,反之亦然,以避免从哈希集合获取对象时出现问题 P> >我个人认为,反之亦然,为了这个目的,让我们考虑在HASMAP中使用一个对象(带有属性)作为密钥,而不需要测试该对象的两个实例之间的相等性。 如果我们以有效的方式覆盖hashcode方法(基于属性和其他规则),那么我们可以拥有唯一的键,在这种情况下,对于HashMap,我们在每个bucket中都有一个唯一的值,HashMap不会使用equals方法来比较bucket中的值,因为每个bucket中都有一个值 合同方:Java 对于有效的散列键,overrided equals不是必需的,java,hashmap,key,equals,hashcode,Java,Hashmap,Key,Equals,Hashcode,在我看到的所有关于重写equals和hashcode方法的问题中,人们总是说,如果重写equals方法,那么也应该重写hashcode方法,反之亦然,以避免从哈希集合获取对象时出现问题 P> >我个人认为,反之亦然,为了这个目的,让我们考虑在HASMAP中使用一个对象(带有属性)作为密钥,而不需要测试该对象的两个实例之间的相等性。 如果我们以有效的方式覆盖hashcode方法(基于属性和其他规则),那么我们可以拥有唯一的键,在这种情况下,对于HashMap,我们在每个bucket中都有一个唯一的
- 我们对象的两个实例是equals(object.equals 1)意味着它们具有相同的引用,并且基于我们的hashcode方法,hashcode将是相同的==>OK
- 不同的hashcode会导致不相等的objects==>NOK(在我们的例子中,有可能打破这个规则),但是由于我们有好的hashing键(我们的场景目的),契约实际上并不重要
- 使用get()方法检索值时出现问题:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
如果我们想根据Baron Steven的帐号检索他的帐号,我们不能只创建一个具有相同帐号的新对象,比如Instance88Obj(144756696)并调用get方法来检索该值。在这种情况下,结果将是null,因为get方法使用基于键引用的比较,即使hashcode相同
- 使用put()方法更新值问题:
First put: [key:Instance1Obj(144756696), value:Person1Obj(“Baron”, “Seven”)]
Second put: [key:Instance2Obj(144756696), value:Person2Obj(“Baron”, “Steven”)]
通常我们希望用“Steven”替换值“Seven”,但这种替换不会发生
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
public V put(K键,V值){
if(key==null)
返回putForNullKey(值);
int hash=hash(key.hashCode());
int i=indexFor(散列,table.length);
for(条目e=表[i];e!=null;e=e.next){
对象k;
如果(e.hash==hash&((k=e.key)==key | | key.equals(k))){
V oldValue=e.value;
e、 价值=价值;
e、 记录存取(本);
返回旧值;
}
}
modCount++;
加法器(散列、键、值、i);
返回null;
}
结论:
==>如果覆盖hashcode,也会覆盖equals
==>如果重写equals,也重写hashcode。(本主题除外:)
如果您的类使用
Object
中的equals
方法,则任何幂等hashCode
实现都可以。如果它使用了除对象
以外的超类中的equals
方法,那么您需要确保您的hashCode
实现与之兼容。可以避免实现equals,也可以避免实现hashCode。在这两种情况下,你都会让自己面对一个充满潜在痛苦的世界。毕竟要记住,散列是用来将对象分割成bucket的,然后bucket中的对象需要检查是否相等。尽管如此,您仍然依赖于正在使用的集合对象,它们的行为完全符合您的需要
这些指导原则是为了防止出现很多问题,除了尝试和变得聪明之外,没有什么好的理由可以避免遵循这些指导原则,这样做会让你自己在未来面临大量问题。不同的哈希代码会导致不相等的对象==>它说的是“引导”,而不是说“总是”。两个相等的对象总是返回相同的哈希代码,但是如果没有重写hashcode方法,两个不相等的对象几乎不会返回相同的哈希代码 合同规定,如果两个对象相等,则它们应该具有相同的哈希代码。但除此之外,如果不相等的对象应该有不同的哈希代码,则哈希表工作得最好 如果覆盖默认的
hashCode
方法而不覆盖默认的equals
方法,则可能会使hashCode
的行为次优;i、 e.给出hashCode为不相等的对象返回相同值的更多情况。这可能会导致哈希表中出现更多冲突,具体取决于hashCode
覆盖的详细信息以及应用程序使用的实例。最终结果将是业绩显著下降
因此,虽然正确的应用程序行为并不严格要求“反之亦然”,但该建议总体上仍然有效。最好是
equal
和hashCode
方法具有匹配的语义 哈希代码的值空间比哈希映射中的桶数大几个数量级。因此,除非我们讨论像EnumMap
这样的专门的Map
实现,否则即使给定了完美的哈希代码,每个bucket也总会有多个对象
不重写equals()
的对象只有在它们是完全相同的对象时才会被视为相等,而不管它们的字段值如何。因此,只重写hashCode()
是毫无意义的,因为它只会带来轻微的性能损失(至少因为自定义哈希代码的计算时间更长)。这甚至可能是一个大的表现
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}