对hashset与hashmap中equals和hashcode的思考

偶然看了f543711700大大写的关于“对java如何判断HashSet和HashMap中相同元素的研究”一文(连接如下)

http://f543711700.iteye.com/blog/800929

对之中提出的“equals返回真,则hashcode值相等”一度抱有怀疑态度,因为之前在实际的编码工作中,确实覆盖了object的equals方法,但从未认真思考hashcode的值是否也相等。

 

为了搞清楚这个问题,首先在JDK源码中看看object是怎么定义equals方法的吧:

 

    Note that it is generally necessary to override the <tt>hashCode</tt>
    method whenever this method is overridden, so as to maintain the
    general contract for the <tt>hashCode</tt> method, which states
    that equal objects must have equal hash codes. 

public boolean equals(Object obj) {
	return (this == obj);
    }

 

 

 

看的出来在object中equals的具体实现就是==,这也是为什么要覆盖这个方法的原因之一,注释的那一段,说明若要覆盖equals方法,同时也要覆盖hashcode方法。力求做到相同的对象有着相同hashcode值。

 

在hashmap中调用put方法时,会进行校验,以确保散列表中没有相同的对象存在,源码如下:

    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;
    }

 在判断是否已经存在这样一个键时,源码里既判断了hashcode的值也使用了equals方法,并且在&&后面的判断中同样也是使用了这两者,只不过是或得关系了。

若是存在这样一个键,会将原来的value覆盖,这就是为什么f543711700文中第二个测试代码size会返回1,因为第一个k-v对已经被第二个k-v对覆盖了。

 

通过与其他同事的交流,只要没有涉及hash相关的代码倒是不用都覆盖hashcode方法,毕竟使用在这种情况下使用它的机会也很少,但是在涉及hash相关的地方一定要覆盖,确保它与equals同步。

 

拿hashset举一个例子吧:

PS:这个例子来源自bepatient大大,写的很恰当,我就借来了

原文链接:http://bepatient.iteye.com/blog/702819

HashSet是一个无序不可以重复储存的集合。HashSet是靠hashcode方法,如果对应的两个对象所返回的hashcode方法的值是相等的,则表明该两个对象“相等”。所以一般情况下,用户需要对要储存到HashSet的对象所在的类重写hashcode方法,而不是用继承自Object的hashcode方法,因为Object 类定义的 hashCode 方法会针对不同的对象返回不同的整数(这一般是通过将该对象的内部地址转换成一个整数来实现的),一般不符合用户的需求。
要想在HashSet中知道对象的位置,就要先计算该对象的hashcode,然后与散列表的列表的总数取余,所得结果就是保存这个元素的列表的索引。
当一个对象被储存进HashSet集合后,就不能再修改这个对象中的那些参与计算hashcode值的属性了,否则对象修改后的hashcode与最初储存到HashSet的值不一致,在这种情况下,即使在contains方法中使用该对象的当前引用作为参数区HashSet集合中检索对象,也将返回不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
为什么会杀不了呢:因为,当集合调用remove方法的时候,会从对象得到hashcode,但是原先对象储存的时候所对应的hashcode不是现在的hashcode,所以定位的不是相同的对象,因此删除不了。
所谓内存泄露是指该对象已经不需要再用,但是一直占着内存空间。

示例代码:

package mm.testJava;
import java.util.*;
public class Person {
    
 private String str;
 private int age;
 
 public Person(){
  
 }
 
 public Person(String str,int age){
  this.str=str;
  this.age=age;
 }
 
 public void setName(String str){
  this.str=str;
 }
 
 public int hashCode(){
  int code;
  
  if(str=="p1") code = 1;
  else if(str=="p2") code = 2;
  else if(str=="p3") code = 3;
  else code = 4;
   
  return code;
 }
}

 

package mm.testJava;

import java.util.Collection;   
import java.util.Date;   
import java.util.HashSet;   
  
public class HashSetTest {   
    public static void main(String[] args) {   
        Collection c = new HashSet();   
  
        Person p1 = new Person("p1",23);   
        Person p2 = new Person("p2",23);   
        Person p3 = new Person("p3",23);   
        c.add(p1);   
        c.add(p2);   
        c.add(p3);   
        p1.setName("p4");   
        c.remove(p1);   
        System.out.println(c.size());//size的值依然是3   
    }   
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值