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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 邱成 于 2012-9-8 23:31 编辑

我想问下HashSet类中的Set.add方法,如果add相同的两个对象,是不是通过equals方法对比后,只在堆内存中生成一个对象?到底是覆盖第一个还是忽略后面的那个呢?我做了个实验,第一步:设定一个Student类,并且重写了该类中的equals方法,返回值的boolean类型为true(因为我要查明是覆盖还是忽略,所以让他不管相同还是不相同都返回true)。第二部:生成HashSet类并向上转型为Set,之后生成两个不同对象并且使用Set.add()添加。具体代码如下

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
class Student
{
String name ;
int age;
public Student()
{
}

public Student(String name, int age)
{
this.name = name ;
this.age = age ;
}
public boolean equals(Object o)
{
System.out.println("equals");
return true;
}
public static void main(String args [])
{
Set set = new HashSet();
Student s1 = new Student("zhangsan",20);
Student s2 = new Student("lisi",21);
System.out.println(s1.equals(s2));
set.add(s1);
set.add(s2);
Iterator it = set.iterator();
while(it.hasNext())
{
Student s = (Student)it.next();
System.out.println(s.name);
}
}
}


结果为:

equals

true

lisi

zhangsan


如果按照我预计的后果应该是lisi覆盖zhangsan,或者输出zhangsan忽略lisi。

难道他调用的不是equals函数来比较的?

7 个回复

倒序浏览
equals()是判读两个Set是否相等。equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
----------------------------------------------------------------------------------------------

        |--Set 元素无序(存储顺序和取出顺序不一致),不可重复。
                |--HashSet
                        底层数据结构是哈希表。线程不安全。
                        如果保证元素的唯一性呢?
                        A:首先根据hashCode值判断。
                        B:如果hashCode值不同,那么,这就是不同的元素。直接存储。
                          如果hashCode值相同,那么,会继续根据equals方法进行判断,
                          根据自己的需求来决定元素是否相同。如果相同,就不存储。否则,存储。

                        一般,用HashSet的时候,要重写hashCode和equals方法。
                        |--LinkedHashSet
                                底层数据结构是链表和哈希表。
                                链表用于保证元素有序,哈希表用于保证元素唯一。
==============================
一个例子:
/*
* HashSet如何保证元素的唯一性?
* 由于HashSet底层数据结构是哈希表,它会根据哈希值进行存储。这个时候,我们如果要实现自己 的需求,
* 判断元素是否唯一,就必须重写hashCode方法,并且重写equals方法。
*
* 当它做元素增加的时候,它会首先判断hashCode值是否相同。当hashCode值不相同,就不用进行equals的判断。
* 这样提高了效率。如果hashCode值相同,这个 时候,它就会自动去根据equals方法进行属性判断。
*
*
*/
public class HashSetDemo {

        public static void main(String[] args) {
                HashSet<Person> set = new HashSet<Person>();

                Person p1 = new Person("陈浩南",28);
                Person p2 = new Person("十三妹",23);
                Person p3 = new Person("陈小春",26);
                Person p4 = new Person("黄秋生",38);
                Person p5 = new Person("黄秋生",38);
                Person p6 = new Person("陈小春",26);
               

                set.add(p1);
                set.add(p2);
                set.add(p3);
                set.add(p4);
                set.add(p5);
                set.add(p6);

                Iterator<Person> it = set.iterator();
                while (it.hasNext()) {
                        Person p = it.next();
                        System.out.println(p.getName() + "***" + p.getAge());
                }
        }

}

public class Person {
        private String name;
        private int age;

        public Person() {
        }

        public Person(String name, int age) {
                this.name = name;
                this.age = age;
        }

        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;
        }

        @Override
        public boolean equals(Object obj) {
                if (this == obj) {
                        return true;
                }

                if (!(obj instanceof Person)) {
                        return false;
                }

                Person p = (Person) obj;

                //System.out.println(p.name + "^^^" + p.age);

                return this.name.equals(p.name) && this.age == p.age;
        }

        @Override
        public int hashCode() {
                // Random r = new Random();
                // return r.nextInt();
               
                //return 1;
               
                //特例:
                //20 + 50
                //30 + 40
               
                return this.name.hashCode()+this.age*17;
        }
}
回复 使用道具 举报
本帖最后由 杨震 于 2012-9-8 23:41 编辑

根据哈希表的实现原理,其实添加对象只是比较对象的hashcode的,计算当存储一个对象时,会根据其hashcode计算其存储地址,如果两个对象hashcode相同,计算出的存储地址必然也相同,这时先添加的会被存入,后添加的由于存储地已经被占,存不进去.所以关键是hashcode


这里讲错了,不只看hashcode,还要看equals,看对象是否equal,二楼讲的很正确
回复 使用道具 举报
楼主想法很好,不过错了,应该是测试hashcode,我也来测试下
回复 使用道具 举报
我错了,经过实验,我发现不只要看hashcode也要看equals,并且是忽略后来相同对象,而不是覆盖;

现在明白为什么重写equals必须保证hashcode也要重写并且保持同步了

楼主再加个hashcode测试

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

我是加了这个方法在里,这样equals相同,hashcode也相同,后面的对象加不进去
回复 使用道具 举报
{:soso_e179:}楼上你答的非常正确
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马