黑马程序员技术交流社区

标题: 框架Hashset的Hashset疑问 [打印本页]

作者: 广驰    时间: 2012-9-6 23:17
标题: 框架Hashset的Hashset疑问
本帖最后由 应广驰 于 2012-9-8 00:14 编辑

毕老师在视频中说,要加入Hashset集合的类必须要复写hashCode()方法和equlas()方法,因为Hashset中比较对象是否唯一就是通过hashCode()方法和equlas()方法

可是正常情况下,每个对象地址的hash值应该都是不一样的吧,hashCode()分配hash地址随机的,但是应该不会分配同一个地址吧
可是毕老师的视频中又有同地址,不同值的情况,说存进Hashset的时候先判断hash地址,地址相同,然后判断内容,内容不同就放在下面,如果hash地址一样,对象不就也一样了么

希望有人能给解释一下地址分配的情况
作者: 武庆东    时间: 2012-9-6 23:20
本帖最后由 武庆东 于 2012-9-6 23:32 编辑


      当向HashSet集合中存入一个元素时,HashSet会调用对象的hashCode()方法来得到对象的hashCode值,然后根据该HashCode值来决定该对象在HashSet对象的存储位置,如果有两个equals()方法比较返回true,但他们的hashCode()方法返回值不相等,HashSet将会把他们的存储在不同位置,也就可以添加成功。HashSet集合判断两个元素相等的标准时两个对象通过equals方法比较相等,并且hashCode()方法返回值相等。如果需要某个类的对象保存到HashSet集合中,重写这个类的equals()方法和hashCode()方法时,应该尽量保证两个对象通过equals比较返回true时,他们的hashcode返回值也相等!

作者: 张飞年    时间: 2012-9-6 23:55
本帖最后由 张飞年 于 2012-9-7 00:13 编辑

hashcode的地址是系统分配的。是否同一元素的判断:在HashSet中元素不可重复,所以先调用系统Object的hashcode方法判断地址值,一般情况下两个元素的地址值是不同的,如果相同了,(它们的存放地址顺序是顺延的)再用系统Object的equals方法判断元素内容是否相同。总之hashcode值不等就直接判断为不同元素,如果相等就再判断equals,如果equals也相等那么就是同一个元素,只能存进去前一个,后者无法存入。如果是存入自定义对象,比如new Person("zhang1",23)之类 的对象时它会调用系统的hashcode /equals方法计算,此时计算的是两个对象的地址值 ,所以它会把两个都 为new Person("zhang1",23)算做是不同的元素,而我们的原意是根据名字与年龄判断是否是同一个元素,所以此时需要重写hashcode/equals两个方法.
作者: AngieFans85    时间: 2012-9-7 00:00
虽说每个对象的Hash值各不相同,但是每个对象放进HashSet中时,HashSet会先根据放进去的对象的Hash码值给对象分配不同的位置以存储对象,如果存放进来的对象多了,HashSet根据自己特有的散列算法,就有可能把两个不同Hash值的对象分配到同一个位置来存放.但是Set容器又规定,不能存放相同的对象.所以,当HashSet中某一个存放位置A已经存放了一个对象,那么一旦另外有元素放进HashSet中,HashSet通过自己的特有算法,把另一个元素暂时安排在了位置A,由于HashSet中同一个位置不可能放两个元素,所以此时,HashSet就要把暂时安排在位置A的元素与之前已经存放在位置A的元素进行equals比较,如果equals返回true,那么这个暂时安排在位置A的元素就不会被放进HashSet中去了.
所以,只要是用HashSet,HashMap,和HashTable这三种容器来存放对象,就一定要重写hashCode()方法和equals()方法.
作者: 黑马-刘心武    时间: 2012-9-7 00:04
在我们往HashSet里面添加对象(add()方法里的参数都是对象)的时候,在Add()的方法内部,它首先调用该对象的hashCode()方法(hashCode方法用来计算该对象的哈希码),如果返回的哈希码与集合已存在对象的哈希码不一致,则add()方法认定该对象没有与集合中的其它对象重复,那么该对象将被添加进集合中。如果hashCode()方法返回的哈希码与集合已存在对象的哈希码一致,那么将调用该对象的equals方法,进一步判断其是否为同一对象。之所以在进行了hashcode(哈希码)的比较后,又调用equals()方法进行比较,是因为虽然HashSet采用的是通过hashcode来区分对象,但是在java中hashcode会重码(即不同的对象,其hashcode可能会相同)。通过hashCode()和equals()方法就能快速且准确的判断在集合中是否存在与添加对象相同的对象。
比如说有两个Student对象 s1和s2.  s1的属性是 s1.name = "李明" , s1.age = 22;  s2的属性是 s2.name = "李明" ,  s2.age = 24; 即学生s1和学生s2同名,但不同年龄.如果出现了s1的hashCode值与s2的相同(重码),会进一步通过equals来判断一下是否为同一元素.而这个equals是由我们定义的, 比如我们定义同名同年龄才是同一个人
可以定义:
        class Student{
            ..........
         public int hashCode(){
              return name.hashCode()+id;
      }
         public boolean equals(Object obj){
         if(obj instanceof Student){
               Student s = (Student)obj;//多态强制转换
               return(name.equals(s.name) && age == s.age);//比较姓名,年龄是否相同,同名同龄才返回真
          }
        }


作者: 王红潮    时间: 2012-9-7 00:26
hashSet集合中底层维护了一个hash表,可以直接通过对象本身定位在hash表中的位置,如果hash值相同,那么就会在原来的值的下面顺延,但这样就降低了效率,视频中是为了讲解方便,刻意定义hash值相同,但对象内容不同,正常情况下,如果两个对象的hash值相等,那么他们的equals方法为true,这也是我们重写hashCode和equals方法要注意的地方。
作者: 杨千里    时间: 2012-9-7 00:41
本帖最后由 杨千里 于 2012-9-7 00:46 编辑

简单的说:
hashCode()是通过对象来确定出对象的哈希码值。
equals()方法是通过比较两个对象的地址值来确定对象是否相等的。
因此默认情况下两个对象是通过比较地址值是否相等来确定两个对象是否相等。

当我们定义一个类,而又想根据对象里面的成员属性来比较两个对象是否相等的话,那么就需要重写这两个方法了

同姓名,同年龄的人有,但是同姓名同年龄同一个地方,同性别,同.....,总没有不同的。如果什么都相同,那就不是人了。如果是人,那么这人就变成鬼了,会分身,java也不允许

你可以这样理解:首先判断他们的地址只是否相等,如果相同,在判断别的。如果不同,则中奖了。所以必须重写
例如:class Person
{
    private String name;
    private int age;
    public Person(String name , int age)
    {
        this.name=name;
        this.age=age;
    }
    public boolean equals(Object obj)  //重写equals方法
    {
            ......................
     }
    public int hashCode()  //重写hashCode方法
    {
         ....................
    }
}

作者: 广驰    时间: 2012-9-8 00:22
马镱洵 发表于 2012-9-7 00:00
虽说每个对象的Hash值各不相同,但是每个对象放进HashSet中时,HashSet会先根据放进去的对象的Hash码值给对象 ...

还是你说的比较详细,是不是可以理解为,哈希表并不是给每个对象分配一个单独对应大小的空间,而是按照哈希表自己的算法,每个空间都是首地址固定且大小也是固定相同的,然后一个只占了空间一部分的对象放进去,如果通过hashCode算出来,另一个对象得到的哈希值一样,还是要放在这个为首地址相同的没有满的空间。然后判断是否相同,不同就放到前一个对象的后面
作者: AngieFans85    时间: 2012-9-8 00:43
应广驰 发表于 2012-9-8 00:22
还是你说的比较详细,是不是可以理解为,哈希表并不是给每个对象分配一个单独对应大小的空间,而是按照哈 ...

简单点说吧,也许和我之前说的略有出入,但以现在的这个说法为准就是了:
就是先将当前要存放进来的对象的Hash码与已经存放进来的对象的Hash码进行比较,如果有相同的Hash码,那肯定会再比较equals()方法,如果equals后也相同,那么当前要存放进来的对象就是肯定存放不进来的.
假如当前要存放进来的对象与已经存放进来的对象进行Hash码比较后不相同,好像就不会再进行equals比较了,而是直接将对象存进来了.
作者: 张榆    时间: 2012-9-8 01:37
这个是根据需求来的,默认是只要new一个对象就有一个独立的哈希值,就是你所说的每个对象都有不同的哈希值;但是比如在描述Person类时,我们的需求是只要名字和年龄都一致就为同一个对象,如果不覆写hashcode方法,就会视为它们是不同对象而存进内存,与需求相矛盾,所以要根据需求覆写hashcode方法,定义符合需求的哈希算法,而哈希算法以需求为依据,是有规律的,算出的值可能相同,可能不同,不同则存入,如果相同,就要进一步判断对象内容是否相同,所以用到equals方法;但是Object中的equals方法时判断对象的地址值是否相同,而我们的需求是只要姓名和年龄相同则为同一对象,所以要覆写equals方法,只要姓名和年龄都一致就返回true。所以再存入元素时,首先是根据hashcode算法算出哈希值,若哈希值不同,则存入,无需覆写并调用equals方法,当哈希值一致时才覆写并调用equals方法。




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