最近在复习的时候,又重新的理解了下equals()和hashCode().
equals()用于判断两个对象是否相等,这是大家公认的。
hashCode()被设计是用来使得哈希容器能高效的工作。


为什么这么说?在java中,有一些哈希容器,比如Hashtable,HashMap等等。当我们调用这些类的诸如get(Object obj)方法时,容器的内部肯定需要判断一下当前对象obj在容器中是否存在,以便进行后续的操作。判断是够存在,肯定是要比较两个对象是否相等,我们"应该"要使用equals()才是正确的。

但是如果哈希容器中的元素有很多的时候,使用equals()必然会很慢。这个时候我们想到一种替代方案就是hashCode()。hashCode()返回一个int类型,比较起来要快很多。但这也造成了一个问题,就是一个判定两个对象事实上存在两个标准?当然不是这样的,正如在文章开头所说,hashCode()被设计用来使得哈希容器中能高效的工作。也只有在哈希容器中,才使用hashCode()来进行比较对象是否相等,而且还是弱的比较。

当我们调用哈希容器的get(Object obj)方法时,他会首先查看当前容器中是够存在相同的哈希值,如果不存在,那么返回null。如果存在,再调用当前对象的equals()方法比较一下看哈希处的对象是否和要查找的对象为相同的对象,如果不是,那么返回null。如果是,说明当前哈希容器存在相应的值,返回该哈希处的对象。

这样hashCode()方法就提高了哈希容器的效率,这就是hashCode()存在的意义。我们可以把hashCode()相等看成是两个对象相等的必要非充分条件,equals()相等才是充分条件。因此,在自定义一个类的时候,我们必须要同时重写equals()和hashCode(),并且必须保证:
1 如果两个对象的equals()相等,那么他们的hashCode()必定相等。
2 如果两个对象的hashCode()不相等,那么他们的equals()必定不等。

 

<pre name="code" class="java">import java.util.HashMap;

public class Demo {
    public static void main(String[] args) throws Exception{
        Person person=new Person("xyz",22);
        HashMap<Person,Integer> hashMap=new HashMap<Person,Integer>();
        hashMap.put(person, 1);
        System.out.println(hashMap.get(new Person("xyz",22)));
    }
}
class Person {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;

    }

//    @Override
//    public int hashCode() {
//        int result = name != null ? name.hashCode() : 0;
//        result = 31 * result + age;
//        return result;
//    }
}
 

上面的代码注释掉hashCode()前后的运行结果不同,当注释掉hashCode()的时候,person和后来新建的Person虽然equals是同一对象,但HashMap容器内部比较hashCode()的时候会认为他们是不同元素。

因此,牢记一点,当我们重写equals()方法时候一定要重写hashCode()方法。下面我们来看看String类的equals方法和hashCode方法:

 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;
    }
 public int hashCode() {
        int h = hash;//默认是0
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;//String的哈希值由组成该String的字符串的ASCII值根据相应的规则加和
    }

 

到知乎阅读最新的技术博客:https://www.zhihu.com/people/hulianwangzhaopin/activities

笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?

提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496

提供给想学习云计算的同学,欢迎收听https://www.zhihu.com/lives/1046567982750281728

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐