A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Zhang_qian 中级黑马   /  2012-5-31 23:17  /  3616 人查看  /  4 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

package collection;

public class Student {
        public static void main(String[] args) {
       

        }
        private String name;
         int age;
        Student(){}
        Student(String name,int age)
        {
                this.age=age;
                this.name=name;
        }
        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }

        public int getAge() {
                return age;
        }
        public void setAge(int age) {
                this.age = age;
        }
        public int hashCode()
        {
                return 23;
        }
        /*public boolean equals(Object obj)
        {
                //System.out.println("**************");
                Student s=(Student)obj;
                return this.name==s.name&&this.age==s.age;
        }*/
        public String toString()
        {
                return name+"***"+age;
        }
       
}上面是我定义的学生类,我只重写它的hashCode()方法,不重写equals方法,如果我在HashSet里面存储是应该去重复的,我把他们的地址值都设置相同了,
当往集合里面存对象时储它会先走hashCode()方法,然后发现hashCode值相同的话就会去调用equals()方法,因为我没重写所以他会调用父类的也就是Object的
equals()方法,而Object里面的equals方法是比较的地址值,因为地址都一样所以就会返回true,也就是不会往里面了存了,但是我测试了一下发现返回的是false,
还是继续往里面存了,所以不明白求高手指点,谢谢了!
**************************************************************************************************************************
下面是测试类:
package collection;
import java.util.HashSet;
public class HashSetDemo {

        public static void main(String[] args) {
                HashSet<Student> hs=new HashSet<Student>();
                Student s1=new Student("zhaoliu",23);
                Student s2=new Student("zhaoliu",23);
                Student s3=new Student("zhaol",23);
                hs.add(s1);
                hs.add(s2);
                hs.add(s3);
                System.out.println(s1.hashCode());//
                System.out.println(s2.hashCode());
                System.out.println(s3.hashCode());
                System.out.println(s1==s2);//为什么返回false?????我已经重写了hashCode()方法了
                System.out.println(s1.equals(s3));//为什么返回false?????我已经重写了hashCode()方法
                System.out.println(hs);

        }

}

4 个回复

正序浏览
首先我们弄清int hashCode()方法和boolean equals()方法:
在set集合中元素不能重复,如果想到比较肯定会首先想到equals方法,但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
hash算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。可以简单这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
回过头来看下javaApi:
  1. public int hashCode()返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
  2. hashCode 的常规协定是:

  3. 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
  4. 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
  5. 如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
  6. 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

复制代码
还有equals方法:
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。

注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

再来看楼主的问题:
首先你复写了hashCode方法,都返回相同的整数,也就是说hashCode都相同,你会认为应该返回true,但是当hashCode相同时候,jvm会自动调用equals方法,这个方法是object的(因为你没复写),调用的是一个引用,当引用相同时返回true;很明显这个引用不同返回false(复写equals会使相同的对象的引用相同);
简单的说:如果两个对象相同,那么它们的hashCode值一定要相同,如果两个对象的hashCode相同,它们并不一定相同 ;
没代码 文字比较多 希望楼主耐心看完 肯定有所明了

回复 使用道具 举报
首先说两点小错误
1,Student类不需要写main函数,
2,你注释部分的equals代码有误 首先要判读if (obj instanceof Student) {Student other = (Student) obj}  else return flase,
return this.name==s.name&&this.age==s.age; 这句有错误 不能用等号,this.name.equals(other.name)

对于你提出的问题一楼回答的很完美,我稍作补充,说明一下地址和hashCode的区别,纯属个人理解

只要你new了个对象,就会在内存中开辟一个空间,这个空间的地址就是一楼说的地址.内存地址
当你new了一个hashSet集合同意也在内存中开辟了一个空间,这个空间有点大.因为大所以要在这个空间上再次划分一下,划分n份,每份地址就是用hashCode表示,.这个地址中可以存放好多个对象,并不是一个hashCode就存一个对象.
每当存放一个对象进集合的时候首先计算出hashCode地址,然后放在集合空间里对应的位置上,再次存放的时候再次计算hashCode值,
如果出现相同,这分空间里判断对象的equals是否相同,比如学生就判断名字是否相同.

大体就是这样的,想更深理解 可以看看张孝祥老师的java高新技术 ,在讲<了解和入门注解的应用>中@override知识点的时候有讲到
回复 使用道具 举报
你只是复写了hashcode方法,即将对象的hashcode值设为一样的了,但对象在内存中的地址值并没有被你改变,假设你能改变地址值,那在内存中一个地址上岂不是有多个数据,这是不可能发生的,对象的地址值虽然也是用hash算法算出来的,但对象的hashCode值不一定等于对象存储的地址值,只是因为用的是同一种数学公式计算而已。
回复 使用道具 举报
楼主,你一连new 了三个对象,怎么能说它们的地址一样呢?
new 了三个对象,就是说s1,s2,s3分别指向了不同的对象(不同的地址)
搞清这点就不难理解了吧
System.out.println(s1==s2);//因为==比较的是地址,跟hashcode没关系,所以返回false
System.out.println(s1.equals(s3));//你重写了hashCode()方法,但跟这句有什么关系呢,人家这里调用的是equals方法,而你又没重写,且默认的equals方法比较的就是地址,故而依然返回false;

至于你说为什么三个都存进去了,因为hashset要先用hashcode 判断,hashcode一样,再用equals判断,前面说了,你只重写了hashcode(),而没重写equals(),默认的
equals方法比较的又是地址,三者地址不同,被认为是三个不同对象,自然就都存进去了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马