黑马程序员技术交流社区

标题: hashset中的hashcode [打印本页]

作者: jiahuiting    时间: 2013-9-26 18:45
标题: hashset中的hashcode
本帖最后由 jiahuiting 于 2013-9-26 20:34 编辑

hashset中比较自定义对象必须复写hashcode方法么,可以只复写equals方法么。
一直不是很懂,这个hashcode是嘛用的

作者: 暮雨    时间: 2013-9-26 18:49
java中hashset跟对象重载的equals和hashcode方法到底有什么关系?

以下面里面分析:
  1. import java.util.HashSet;

  2. public class EmployeeTest
  3. {
  4.         static int count = 0;
  5.         static public class Demo
  6.         {
  7.                 public Demo(String s){
  8.                         mStr = s;
  9.                 }
  10.                 public boolean equals(Object object){
  11.                         boolean b = mStr.equals(((Demo)object).mStr);     // 尝试修改代码返回true或false
  12.                         return b;
  13.                 }
  14.                 public int hashCode(){
  15.                         return ++count;  // 尝试修改代码返回定值或变值
  16.                 }
  17.                 public String mStr;
  18.         }
  19.         public static void main(String[] args)
  20.         {
  21.                 HashSet<Demo> set = new HashSet<Demo>();
  22.                 boolean b = false;
  23.                 b = set.add(new Demo("aa"));  // hashset的add方法文档中指出当 !obj1.equals(obj2)   时才会进行插入
  24.                 b = set.add(new Demo("bb"));
  25.                 b = set.add(new Demo("bb"));
  26.                 System.out.print(set.size());
  27.         }
  28. }
复制代码

如果hashcode函数返回一个定值,即所有对象的hashcode相同时,当尝试插入第二个数据(或以上)时,会发现程序还会去调用equals方法,并且该函数返回false时才回插入hashset;当hashcode返回不同时会忽略equals函数并直接插入hashset。

总结,hashset先调用对象的hashcode函数来进行散列,当散列到不同位置时,则认为对象不相同且进行插入操作(不用判断equals函数);当散列到同一个位置才会调用对象的equals函数来进行比较,只当equals返回false则认为两个对象不相等才会进行插入操作,否则认为两个对象相同而不进行插入操作。

自定义对象,重载equals和hashcode时,应该保证两个对象的hashcode的比较结果,和,两个对象的equals函数比较结果,一样。
本例子只是为了说明问题。

刚刚开始使用java语言,以上都是通过调试数据推断出来的,更好的办法应该是读源代码。如果有错,欢迎指出。
作者: 张云飞    时间: 2013-9-26 19:22
楼上讲的很好的啦。
首先,hashCode是依据元素在内存中的地址,经过某种算法得到的。因此,两个对象肯定有不同的哈希值。
HashSet集合底层数据结构式哈希表,无序,不能存入重复的元素。
这里怎样才是所谓的“重复元素”?或者说HashSet内部如何去判断连个元素是不是重复的?
HashSet依据的正是两个方法:hashCode()和equals()。
并且是先判断使用hashCode()方法判断,如果返回true,也就是两个元素的hashCode相同时,才会调用equals()方法。

知道了HashSet的判断原理,我们便可以根据自己的需要去自定义覆盖两个方法了。
所以,其实覆盖哪一个都可以的,只是结果不一样了。
比如:
String str1 = new String("abc");
String str2 = new String("abc");
对于这样两个字符串,你必须明白它们的hashCode是不一样的,两个对象嘛。

这样,如果想往HashSet中存入这两个元素,正常情况下那是没门儿的。
所以嘛,嘿嘿,这个时候你就必须自己覆盖掉hashCode()方法了么。让这个方法不依靠哈希值判断,这不OK了。

作者: HM代景康    时间: 2013-9-26 19:45
object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象
(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,
当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,
导致混淆,因此,就也需要重写hashcode()

举例说明:



[java] view plaincopyprint?
01.import java.util.*;  
02.  
03.public class HelloWorld {  
04.    public static void main(String[] args) {  
05.        /*
06.        Collection c = new HashSet();
07.        c.add("hello");
08.        c.add(new Name("f1","l1"));
09.        c.add(new Integer(100));
10.        c.remove("hello");  
11.        c.remove(new Integer(100));
12.        System.out.println(c.remove(new Name("f1","l1")));
13.        */  
14.        Name n1 = new Name("01");  
15.        Name n2 = new Name("01");  
16.         
17.        Collection c = new HashSet();  
18.        c.add(n1);  
19.        System.out.println("------------");  
20.        c.add(n2);  
21.        System.out.println("------------");  
22.        System.out.println(n1.equals(n2));  
23.        System.out.println("------------");  
24.        System.out.println(n1.hashCode());  
25.        System.out.println(n2.hashCode());  
26.        System.out.println(c);  
27.    }  
28.  
29.  
30.}  
31.  
32.class Name {  
33.    private String id;  
34.    public Name(String id) {  
35.        this.id = id;   
36.    }  
37.      
38.    public String toString(){  
39.        return this.id;  
40.    }  
41.    public boolean equals(Object obj) {  
42.        if (obj instanceof Name) {  
43.            Name name = (Name) obj;  
44.            System.out.println("equal"+ name.id);  
45.            return (id.equals(name.id));  
46.        }  
47.        return super.equals(obj);  
48.    }  
49.         
50.    public int hashCode() {  
51.        Name name = (Name) this;  
52.        System.out.println("Hash" + name.id);  
53.        return id.hashCode();  
54.              
55.    }  
56.}  
import java.util.*;

public class HelloWorld {
    public static void main(String[] args) {
        /*
        Collection c = new HashSet();
        c.add("hello");
        c.add(new Name("f1","l1"));
        c.add(new Integer(100));
        c.remove("hello");
        c.remove(new Integer(100));
        System.out.println(c.remove(new Name("f1","l1")));
        */
        Name n1 = new Name("01");
        Name n2 = new Name("01");
        
        Collection c = new HashSet();
        c.add(n1);
        System.out.println("------------");
        c.add(n2);
        System.out.println("------------");
        System.out.println(n1.equals(n2));
        System.out.println("------------");
        System.out.println(n1.hashCode());
        System.out.println(n2.hashCode());
        System.out.println(c);
    }


}

class Name {
    private String id;
    public Name(String id) {
        this.id = id;
    }
   
    public String toString(){
            return this.id;
    }
    public boolean equals(Object obj) {
            if (obj instanceof Name) {
                Name name = (Name) obj;
                System.out.println("equal"+ name.id);
                return (id.equals(name.id));
            }
            return super.equals(obj);
        }
               
        public int hashCode() {
                Name name = (Name) this;
                System.out.println("Hash" + name.id);
                return id.hashCode();
                    
        }
}


就这个程序进行分析,在第一次添加时,调用了hashcode()方法,将hashcode存入对象中,第二次也一样,然后对hashcode进行比较。hashcode也只用于HashSet/HashMap/Hashtable类存储数据,所以会用于比较,需要重写

总结,自定义类要重写equals方法来进行等值比较,自定义类要重写compareTo方法来进行不同对象大小的比较,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable类时进行比较


作者: jiahuiting    时间: 2013-9-26 20:37
暮雨 发表于 2013-9-26 18:49
java中hashset跟对象重载的equals和hashcode方法到底有什么关系?

以下面里面分析:

清楚了,谢谢哦。还有个问题请教下,在重写equals和hashcode的时候,必须放在主函数中么,这个放在哪个类有规定嘛?

作者: 欲困    时间: 2013-9-26 21:43
本帖最后由 欲困 于 2013-9-26 21:45 编辑

equals呢是比较对象内的值用的,但是它并不能比较这两个对象是否一样。hashSet存储对象是都为每一个对象分配了一个哈希码,然后存储到不同的区域(相当于分批存储)如果两个对象值一样但它所存的区域不一样 equals是不能进行比较的,这时候就要用到hashcode进行比较了。




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