黑马程序员技术交流社区

标题: 请教:为何要覆盖hashCode 和equals方法?具体说说 [打印本页]

作者: 王婷婷    时间: 2013-4-13 16:16
标题: 请教:为何要覆盖hashCode 和equals方法?具体说说
本帖最后由 王婷婷 于 2013-4-14 15:08 编辑

在集合存储时 ,为何要覆盖hashCode 和equals方法?就只为了自定义自己的比较方法?具体说说

作者: 殇_心。    时间: 2013-4-13 16:30
你说对了。。  就为定义自己的方法。
有些时候 比如你得按什么要求排序啊  什么要求比较啊
你就得复写hashCode和equals方法。
作者: 王洪宇    时间: 2013-4-13 16:32
Set集合:集合中的元素不可重复。
在向HashSet中写入元素时需要进行检查,看看集合中是否已经存在将要写入的元素。如果存在,则不能写入。
如何检查集合中某个元素是否等于将要存入的元素?
两个对象的equal()方法比较相等,并且两个对象的hashCode()方法返回值也相等,则两个对象相等,将不能写入。
向HashSet中存入自定义对象时,因为HashSet要检查对象是否相等,要调用hashCode()方法确定对象的存储位置,所以对象类中需要重写两个方法。

重写原则:
1.在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值;
2.当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等;
3.对象中用作 equals() 方法比较的属性,都应该用来计算 hashCode 值。

作者: 王婷婷    时间: 2013-4-13 16:38
殇_心。 发表于 2013-4-13 16:30
你说对了。。  就为定义自己的方法。
有些时候 比如你得按什么要求排序啊  什么要求比较啊
你就得复写hashC ...

嗯,谢谢
作者: 王婷婷    时间: 2013-4-13 16:38
王洪宇 发表于 2013-4-13 16:32
Set集合:集合中的元素不可重复。
在向HashSet中写入元素时需要进行检查,看看集合中是否已经存在将要写入 ...

嗯,谢谢嘞,,
作者: huima    时间: 2013-4-13 16:44
由于默认的equals()方法是使用是否同一对象的引用来判断是否相等,由于默认的hashCode()返回对象的内存地址,而不同的对象有不同的地址,因此,如果未覆盖equals(),也不需覆盖hashCode()。但有一点需注意,由于内存地址比Integer的数值要大,因此不同地址所转换成的int值可能会一样。即使出现这种情况也不要紧,仅影响hashtable的运行效率而已。
hashCode()的返回值必须根据用于equals()判断的信息来决定。如果equals()返回true,hashCode()必须返回相同的int值。如果equals()为false,最好返回不同的int值。

作者: 张洪慊    时间: 2013-4-13 17:08
在创建自定义类时->可能会存入HashSet集合->HashSet底层用的是HashMap集合
->而HashMap:  为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。(HashTable和HashMap均有这个特性)
   (如果你学过数据结构hash表的话,更好理解)

作者: 殇_心。    时间: 2013-4-13 17:10
王婷婷 发表于 2013-4-13 16:38
嗯,谢谢

都是菜鸟。。 别说谢
作者: 李易烜    时间: 2013-4-13 17:12
本帖最后由 李易烜 于 2013-4-13 17:16 编辑

当我们重写了对象的equals方法,一般情况下(这里我指这些对象不需要放到Set或Map中仅仅是比较需要,或者虽然放到Set或Map中,但是get和set时用的是同一对象)是没有问题的,但是,有些情况下就不同了;举个例子
  1. public class Person(){

  2. private int id;

  3. private String name;

  4. //define getter and setter here, omited



  5. public void equals(Object obj){

  6. if (!(obj instanceof Person))
  7.             return false;
  8. return super.equals(obj) && ((Member) obj).getId() == this.getId();

  9. }

  10. }
复制代码
上述class中没有覆盖hashCode方法,因此这个时候用的是Object.hashCode()的返回值;我们来测试一下
  1. public class TestHashCode extends TestCase {
  2.         public void testMap() {
  3.         Person p1 = new Person(1,"aaa"),p2 = new Person(1,"bbb");
  4.         Map map = new HashMap();
  5.         map.put(p1,p1);
  6.         Member value = (Person)map.get(p2);
  7.         System.out.println(value.getName());
  8.     }
  9. }
复制代码
请注意,这两个对象是相等的,可以从equals()的定义看出来;运行TestHashCode ,结果是NullPointerException;

现在添加hashCode()到Person中
  1. public int hashCode() {
  2.         
  3.         return this.getId() * 37 ;
  4.     }
复制代码
重运行TestHashCode ;huh,我们想要的结果出现了,“aaa”

为什么添加person.hashCode()前,得不到person对象;因为java中默认的hashCode是根据对象的地址计算得到的,虽然p1.equals(p2)=true,但是p1,p1有不同的内存地址,所以有不同的hashCode;所以通过p2是不能得到value的,这个时候value==null;添加hashCode()后,两个对象有相同hashCode,所以能得到

java中Set是通过Map实现的,所以Map和Set的所有实现类都要注意这一点

HashMap是通过链地址法解决hash collision的,并且新对象都是添加到表头的(这个看了好久才明白,数据结构都还老师了)
作者: 杨修    时间: 2013-4-13 17:53
不同的集合内数据结构不一样,不同的需求需要不同的排序,不同的存储方式,复写hashCode()就是满足对象在集合中的不同存储位置。而Set集合中元素不能重复,复写equals()方法主要是根据需求和标准判断元素不重复。。。
作者: 花伟昌    时间: 2013-4-13 20:56
HashSet集合特点:元素不能重复,无序(也可能有序).HashSet集合通过哈希算法确定了元素在集合中的位置。
但是,在HashSet集合中存储自定义对象的时候,发现同一个对象可以重复存在。
这是因为,哈希表在确定元素是否相同主要判断两个方面:
1,判断两个元素的的哈希值是否相同,如果相同就不判断两个对象的内容是否相同。
2,判断两个对象是否相同,实际上是调用了对象的也就是Object 的equals()方法,因为,对象的equals()方法比较的是两个对象的地址。
每个对象的地址是不同的,所以这样会使HashSet集合会误认为是不同的元素,这样就会存储自定义的对象。
 
  
  
通过覆写hashCode()和equals()方法,使其可以比较元素的哈希值和元素的内容。保证元素的唯一性。


作者: lyg2013    时间: 2013-4-13 21:26
在Object类中定义了hashCode()和equals()方法,Object类的equals()方法按照内存地址比较对象是否相等,因此如果 object1.equals(object2)为true,表明object1变量和object2变量实际上引用同一个对象,那么object1和 object2的哈希码肯定也相同,也就是说object1.hashCode()==object2.hashCode()

如果用户定义的类覆盖了Object类的equals()方法,但是没有覆盖Object类的hashCode()方法,就会导致当 object1.equals(object2)为true 时,而 object1 和 object2的哈希码不一定一样。

所以如果覆盖了equals()方法,也应该覆盖hashCode()方法,并且保证2个相等的object对象的哈希码也一样
  1. 代码:

  2. ObjectChild覆盖Object类的equals()方法

  3. public boolean equals(Object o){
  4. if(this==0) return true;
  5. if(! (o instanceof ObjectChild)) return false;
  6. final ObjectChild other = (ObjectChild) o ;

  7. if(this.name.equals(other.getName()) && this.age == other.getAge())
  8. //这里假定的 ObjectChild 是一个JavaBean 对象,含有 name 和 age 2个属性,如果name和age相同,则看为同一个对象
  9. return true;
  10. else
  11. return false;
  12. }

  13. ObjectChild覆盖Object类的hashCode()方法

  14. //返回自定义方法计算出的hashCode值,使得拥有相同属性的对象拥有相同的hashCode;
  15. public int hashCode(){
  16. int result;
  17. result = (name==null?0:name.hashCode());
  18. result = 29*result + (age==null?0:age.hashCode());
  19. return result;
  20. }
复制代码

作者: 黑马李杰    时间: 2013-4-15 21:24
不是集合比较的时候就覆盖hashCode 和equals方法这两个方法,如果是List集合存储了,自定义对象的时候,要复写equals方法,因为我们要是对集合中的元素进行,remove和比较是否相等的时候,底层调用了这个方法,所以我们要定义比较相等的方式。如果是HashSet集合,才要复写这两个方法,因为HashSet集合是无序的,且不能装重复元素。如何判断重复元素呢,HashSet底层是哈希表结构,在内存中,通过哈希值把内存分成好几份。HashSet在存储数据的时候会先去这些区域中找是否有指定的元素。如果有才会用equals方法比较这两个元素是否相等。所以要复写这两个方法。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2