Java的equals() 和 hashCode()

首先,equals()方法和hashCode()方法都来自于Object类的定义,Java类都继承了这两个方法,都定义了本身的实现。java

equals:

equlas()方法的正确理解应该是:判断两个对象是否相等。那么判断对象相等的标尺又是什么?算法

在object类中,此标尺即为==。ide

public boolean equals(Object obj) {
        return (this == obj);
    }

固然,这个标尺不是固定的,其余类中能够按照实际的须要对此标尺含义进行重定义。如String类中则是依据字符串内容是否相等来重定义了此标尺含义。性能

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

如此能够增长类的功能性和实际编码的灵活性。固然了,若是自定义的类没有重写equals()方法来从新定义此标尺,那么默认的将是其父类的equals(),直到Object基类。ui

以下场景的实际业务需求,对于User bean,由实际的业务需求可知当属性uid相同时,表示的是同一个User,即两个User对象相等。则能够重写equals以重定义User对象相等的标尺。this

package com.corn.objectsummary;

public class User {

    private int uid;
    private String name;
    private int age;

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    protected 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 (obj == null || !(obj instanceof User)) {
            return false;
        }
        if (((User) obj).getUid() == this.getUid()) {
            return true;
        }
        return false;
    }
}
package com.corn.objectsummary;

public class ObjectTest implements Cloneable {

    public static void main(String[] args) {
        User u1 = new User();
        u1.setUid(111);
        u1.setName("张三");

        User u2 = new User();
        u2.setUid(111);
        u2.setName("张三丰");

        System.out.println(u1.equals(u2)); //返回true
    }

}

ObjectTest中打印出true,由于User类定义中重写了equals()方法,这很好理解,极可能张三是一我的小名,张三丰才是其大名,判断这两我的是否是同一我的,这时只用判断uid是否相同便可。编码

重写equals()方法遵循的准则:spa

  • 对称性:若是x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。code

  • 反射性:x.equals(x)必须返回是“true”。对象

  • 类推性:若是x.equals(y)返回是“true”,并且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。

  • 还有一致性:若是x.equals(y)返回是“true”,只要x和y内容一直不变,无论你重复x.equals(y)多少次,返回都是“true”。

  • 任何状况下,x.equals(null),永远返回是“false”;x.equals(和x不一样类型的对象)永远返回是“false”。

如上重写equals方法表面上看上去是能够了,实则否则。由于它破坏了Java中的约定:重写equals()方法必须重写hasCode()方法。

hashCode:

hashCode():方法返回一个整形数值,表示该对象的哈希码值。

hashCode()具备以下约定:

1).在Java应用程序程序执行期间,对于同一对象屡次调用hashCode()方法时,其返回的哈希码是相同的,前提是将对象进行equals比较时所用的标尺信息未作修改。在Java应用程序的一次执行到另一次执行,同一对象的hashCode()返回的哈希码无须保持一致;

2).若是两个对象相等(依据:调用equals()方法),那么这两个对象调用hashCode()返回的哈希码也必须相等;

3).反之,两个对象调用hasCode()返回的哈希码相等,这两个对象不必定相等。

即严格的数学逻辑表示为: 两个对象相等 <=>  equals()相等  => hashCode()相等。所以,重写equlas()方法必须重写hashCode()方法,以保证此逻辑严格成立,同时能够推理出:hasCode()不相等 => equals()不相等 <=> 两个对象不相等。

可能有人在此产生疑问:既然比较两个对象是否相等的惟一条件(也是次要条件)是equals,那么为何还要弄出一个hashCode(),而且进行如此约定,弄得这么麻烦?

其实,这主要体如今hashCode()方法的做用上,其主要用于加强哈希表的性能。

以集合类中,以Set为例,当新加一个对象时,须要判断现有集合中是否已经存在与此对象相等的对象,若是没有hashCode()方法,须要将Set进行一次遍历,并逐一用equals()方法判断两个对象是否相等,此种算法时间复杂度为o(n)。经过借助于hasCode方法,先计算出即将新加入对象的哈希码,而后根据哈希算法计算出此对象的位置,直接判断此位置上是否已有对象便可。(注:Set的底层用的是Map的原理实现)

在此须要纠正一个理解上的误区:对象的hashCode()返回的不是对象所在的物理内存地址。甚至也不必定是对象的逻辑地址,hashCode()相同的两个对象,不必定相等,换言之,不相等的两个对象,hashCode()返回的哈希码可能相同。

所以,在上述代码中,重写了equals()方法后,须要重写hashCode()方法。

package com.corn.objectsummary;

public class User {

    private int uid;
    private String name;
    private int age;

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    protected 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 (obj == null || !(obj instanceof User)) {
            return false;
        }
        if (((User) obj).getUid() == this.getUid()) {
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + this.getUid();
        return result;
    }
}

注:上述hashCode()的重写中出现了result*31,是由于result*31 = (result<<5) - result。之因此选择31,是由于左移运算和减运算计算效率远大于乘法运算。固然,也能够选择其余数字。

相关文章
相关标签/搜索