黑马程序员技术交流社区

标题: HashSet的哈希值问题 [打印本页]

作者: wubing    时间: 2014-7-20 23:12
标题: HashSet的哈希值问题
今天刚听老师讲到集合时,提到一个问题,有点不解?求详细解答

如果两个对象的哈希值一样,两个对象的equals()一定返回真吗?
两个对象的equals方法如果返回真,两个对象必须具有相同的哈希值吗?


作者: sugar    时间: 2014-7-21 08:44
如果hashCode()的两个值相同,才会判断equals()是否为true;

如果元素的hashCode()值不同,不会调用equals();
作者: zeus00456    时间: 2014-7-21 08:53
本帖最后由 zeus00456 于 2014-7-21 08:56 编辑

第一句话是毫无疑问的不确定,因为每个类评判equals的方式不同。比如,String在判断是否equals时是通过比较内容,而如果自定义一个类Person,这个类的对象默认的从Object类中继承的equals方法就是比较哈希值。对应前者,哈希值相同而内容不同时,equals返回false;而后者则返回true(因为)Person实例的equals就是判断哈希值,而哈希值作为这个问题的前提,已经相同了
第二句话可以分两个方面来回答:
1.从纯粹的代码角度看——答案是不确定
举一个最简单的例子
chares[] cs = {/*这里的内容不为空,但是具体内容随便*/}
String s = new String(cs);
String t = new String(cs);
因为s/t是new出来的两个独立的对象(请注意区分 String r = "/*字符串内容*/"),我们它们的内容是一样的,所以equals返回true
我们假设,如果它从Object中继承而没有复写hashCode()方法(实际上它复写了),那么,此方法返回的哈希值对于两个对象是不同的
而如果我们用的不是String,而是自己的类,比如依旧拿Person举例子好了,这个类的对象的哈希值就是不同的
2.从实际的角度——答案是确定的
我们继续黑Person类吧,假如我们自定义Person类,我们不复写hashCode()方法,但是我们复写equals方法,让我们创建的Person对象的成员属性一旦一致,equals方法就会返回true(但是没复写hashCode,哈希值不同)这是完全可以的。
但是在实际开发中这种情况应该是极少见甚至不被允许的。
返回上面String的例子,上面说String类的hashCode方法已经被复写了,那么复写效果是什么呢?


很明显,复写的效果是:只有字符串内容一样,哈希值就一样,而且前面也说了,equals也一样
我们来看一下API中关于这两个方法的描述




到此为止我们说的其实已经很清楚了:
对于问题的上半截,答案是不确定
对于问题的下半截,如果真想用代码用共能去实现,我们完全可以做到使equals返回true,同时让hash值不同
但是,在实际开发中,没有人这么干,而且好像也不允许这么干


作者: 薛振宇    时间: 2014-7-21 12:36
我必须说一下,昨天纠结了好长时间的,首先如果你这个类,不重写equals和hashcode这时候:
   ==和equals是一样的,判断的都是地址值是否相等。地址值和哈希值相关,地址值一样,哈希值就一样。
这是基本状况,当时如果你重写了hashcode方法就像视频里一样,那么hash值哪怕相同,equals也是不同。同理
你重写了equals,那么哪怕equals相等,hash值也是不同。这里涉及到一个重写的问题。
  String类就常有这种困惑:
    String s1=“aaa”;String s2=“aaa”; s1==s2  s1.equals(s2)这都是true,这是因为String的存储,有了一个aaa,那么下一个s2引用指向这个aaa。地址值是一样的。
  String s1=new String(“aaa”); String s2=new String(“aaa”); s1==s2  s1.equals(s2)
第一个是false,第二个是true,equals不是比较地址值吗?两个对象不是肯定有不同地址值吗?这是因为
String重写了equals方法,当我们s1.hashCode,s2.hashCode,两个值不一样,地址是不一样的。
注意:equals默认比较的是地址,而不是hash值。
说说这个集合,HashSet,两步判断方法,先hash值,在equals方法。如果你不重写方法的话,那么
hash值相等,equals就是true。当你new两个对象时,hash值是肯定不同的,哪怕其具有了相同的成员变量,
至于重写后,你就能解决这个问题了。楼主如果没看明白,就拿代码试,或者再问我
作者: 薛振宇    时间: 2014-7-21 12:42
zeus00456 发表于 2014-7-21 08:53
第一句话是毫无疑问的不确定,因为每个类评判equals的方式不同。比如,String在判断是否equals时是通过比较 ...

你说的equals方法默认比较的是hash值,这个好像是不对的,实验Person类,重写hashCode方法,返回一个默认值,new两个Person类对象,使用equals方法,他们是不等的。
作者: 薛振宇    时间: 2014-7-21 12:44
sugar 发表于 2014-7-21 08:44
如果hashCode()的两个值相同,才会判断equals()是否为true;

如果元素的hashCode()值不同,不会调用eq ...

是的,两步的比较过程
作者: 薛振宇    时间: 2014-7-21 12:50
public class TestPerson {
        public static void main(String[] args) {
                Person p1=new Person("xue");
                Person p2=new Person("shi");
                System.out.print(p1.equals(p2));
               
        }
}
  class Person{
          String s=null;

        @Override
        public int hashCode() {
               
                return 50;
        }

        @Override
        public boolean equals(Object obj) {
                // TODO Auto-generated method stub
                return super.equals(obj);
        }

        public Person(String s) {
                super();
                this.s = s;
        }
  }如果equals比较的hash值,那么p1.equals(p2)应该为真
作者: chen010yu    时间: 2014-7-21 21:25
过来学点知识
作者: 薛振宇    时间: 2014-7-21 21:35
有人能告诉我,我说的是对的吗,发现错了,给我指出来,谢谢。

作者: 王石    时间: 2014-7-21 23:17
我想说了,看的有怎么多人回答的怎么好,我不好意思说了
作者: 楚风★憧憬    时间: 2014-7-23 13:51
回答的很好啊  有总结能力啊

作者: Stevenj    时间: 2014-7-23 14:06
zeus00456 发表于 2014-7-21 08:53
第一句话是毫无疑问的不确定,因为每个类评判equals的方式不同。比如,String在判断是否equals时是通过比较 ...

回答的很棒,支持,这样细心的回答,顶一个




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