Java中对象比较

主讲人:王少华  QQ群号:483773664

学习目标:

掌握java中的==、equals()、hashCode()的异同

一、问题

通过前面的学习,我们知道,Java集合有三个大的接口,List接口、Map接口、Set接口,这三个接口的特点是List接口中的元素能重复、Map接口中的key对象不能重复,Set接口中的元素是不可以重复的。那么问题来了,两个元素是否重复是根据什么来判断的。

二、Java中两个对象比较

(一)、==

1、Java中,比较简单类型变量用“==”,只要两个简单类型值相等即返回ture,否则返回false;

2、Java中,引用类型比较 ==,表示的是引用地址的比较。在用“==”比较引用类型时,仅当两个应用变量的对象指向同一个对象时,才返回ture。言外之意就是要求两个变量所指内存地址相等的时候,才能返回true,每个对象都有自己的一块内存,因此必须指向同一个对象才返回ture。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestDengHao {
    public static void main(String[] args) {
        int a = 12;
        int b = 12;
        System.out.println(a==b);
        Integer aA = 12;
        Integer bB = 12;
        System.out.println(aA==bB);
        Integer A = new Integer(12);
        Integer B = new Integer(12);
        System.out.println(A==B);
    }
}

上述代码运行结果是


A和B是两个不同的对象(尽管某个成员的值相等),所以结果是false。

(二) equals

1、equals()方法来自于Object类,每个自定义的类都可以重写这个方法。Object类中的equals()方法仅仅通过“==”来比较两个对象是否相等,即默认情况下,equals和==效果相同

1
2
3
4
public class Person {
    private int id;
    private String name;
}
1
2
3
4
5
6
7
8
public class TestEquals {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        System.out.println("==:"+(p1==p2));
        System.out.println("equals:"+p1.equals(p2));
    }
}

上述代码运行效果


(三)、重写equals

如果需求要求,只要是id相同的,表示的是同一个人。为了实现这个需求,我们需要重写equals方法,重写的一般规则如下

1、先用“==”判断是否相等。 

2、判断equals()方法的参数是否为null,

如果为null,则返回false;因为当前对象不可能为null,如果为null,则不能调用其equals()方法,否则抛java.lang.NullPointerException异常。

当参数不为null,则如果两个对象的运行时类(通过getClass()获取)不相等,返回false,否则继续判断。

3、判断类的成员是否对应相等。

本需求只要求比较id,所以重写equals代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (id != other.id)
            return false;
        return true;
    }
1
2
3
4
5
6
7
8
9
10
public class TestEquals {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setId(1);
        Person p2 = new Person();
        p2.setId(1);
        System.out.println("==:"+(p1==p2));
        System.out.println("equals:"+p1.equals(p2));
    }
}

上述代码运行效果


(四)、hashcode()

hashCode是根类Obeject中的方法,是一个本地方法,它的实现是根据本地机器相关的。默认情况下,Object中的hashCode() 返回对象的32位jvm内存地址。也就是说如果对象不重写该方法,则返回相应对象的32为JVM内存地址。

1
2
3
4
5
6
7
8
9
10
public class TestEquals {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setId(1);
        Person p2 = new Person();
        p2.setId(1);
        System.out.println("p1.hashCode:"+p1.hashCode());
        System.out.println("p2.hashCode:"+p2.hashCode());
    }
}


(五)、类中的equals和hashCode

1、Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的

2、结论:equals()相等的两个对象,hashcode()一定相等,equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

3、理解:equals相等,说明内存地址是一样,那么hashCode肯定一样;

          eqauls不相等,说明内存地址是不一样的,又因为hashCode是机器生成的,有可能在生成的时候产生冲突造成的。

三、在java的集合中

(一)判断两个对象是否相等的规则

Java系统首先调用对象的hashCode()方法获得该对象的哈希码表,然后根据哈希吗找到相应的存储区域,最后取得该存储区域内的每个元素与该对象进行equals方法比较。

具体规则如下:

1,判断两个对象的hashCode是否相等 

      如果不相等,认为两个对象也不相等,完毕 

      如果相等,转入2

2),判断两个对象用equals运算是否相等 

      如果不相等,认为两个对象也不相等 

      如果相等,认为两个对象相等。 

为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。 

(二)应用hashCode和equals

1、没有重写这二个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {
    private int id;
    private String name;
    public Person(){}
    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestHashSet {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new Person(1, "zhangsan"));
        hs.add(new Person(2, "lisi"));
        hs.add(new Person(3, "wangwu"));
        hs.add(new Person(1, "zhangsan"));
        Iterator it = hs.iterator();
        while (it.hasNext()) {
            Person person = (Person) it.next();
            System.out.println(person.getId()+":"+person.getName());
        }
    }
}


运行结果

分析:因为在根据hashcode()对两次建立的new Student(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,所以hashset把他当作不同的对象对待了,当然此时的equals()方法返回的值也不等。

2、重写这二个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return id*name.hashCode();
    }
    @Override
    public boolean equals(Object obj) {
         if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Person other = (Person) obj;
            if (id != other.id)
                return false;
            return true;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestHashSet {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new Person(1, "zhangsan"));
        hs.add(new Person(2, "lisi"));
        hs.add(new Person(3, "wangwu"));
        hs.add(new Person(1, "zhangsan"));
        Iterator it = hs.iterator();
        while (it.hasNext()) {
            Person person = (Person) it.next();
            System.out.println(person.getId()+":"+person.getName());
        }
    }
}

上述代码运行效果


可以看到重复元素的问题已经消除。

四、总结

1、默认。

==默认比较对象在JVM中的地址。

hashCode 默认返回对象在JVM中的存储地址。

equal比较对象,默认也是比较对象在JVM中的地址,同==

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

3、Java对象比较,一般情况下都要重写hashCode和equals二个方法

五、视频地址

http://edu.51cto.com/course/course_id-6028.html