基础知识点-Object类的hashCode方法(理解equals与hashCode)

hashCode方法用于散列存储,减少equals比较带来的效率问题。若两个对象equals相等,hashCode必须相等;不等时可能相等。覆盖equals时需同时覆盖hashCode,保证散列集合正确工作。未重写hashCode,即使equals相等,对象在散列集合中也无法找到。文章通过示例解释了不重写hashCode可能导致的问题。

在Java的Object类中有一个方法:

public native int hashCode();

根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中没有给出具体的实现。

hashCode方法的作用

hashcode是java中Objet类定义的方法,默认返回的是对象的内存地址(主要用途是在对对象进行散列的时候作为key输入)。但该方法的本意是散列。散列的话就必然涉及到在一定的空间中进行散列,所以hashcode方法一定是和集合配合使用的时候才用得到。

Java中的集合有两类,一类是List,另一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可以重复。

那么要想保证元素不重复,可两个元素重复应该依据什么来判断呢?

很多人都会想到equals方法来逐个进行比较,这个方法可行。但是如果集合中有数量庞大的数据,效率会成为一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果该table中没有该hashcode值,就可以直接存进去,不需要进行任何比较了。如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决问题,实际调用equals方法的次数就大大降低了。

equals方法与hashCode方法关系

  1. 如果两个对象equals,Java运行时环境会认为它们的hashcode一定相等。
  2. 如果两个对象不equals,它们的hashcode有可能相等。
  3. 如果两个对象hashcode相等,它们不一定equals。
  4. 如果两个对象hashcode不相等,它们一定不equals。

覆盖equals时总要覆盖hashCode

《Effictive Java》中提到:在每个覆盖equals方法的类中,也必须覆盖hashCode方法。如果不这样做,就会违反Object.hashCode的通用规定,从而导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包含HashMap,HashSet和HashTable。

约定的内容是:

  • 在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
  • 如果两个对象根据equals方法的比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生相同的整数结果。
  • 如果两个对象的equals方法比较是不相等的,那么调用这两个对象中任意一个对象hashCode方法,则不一定要产生不同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的结果,有可能提高散列表的性能。

因没有覆盖hashCode而违反的关键约定是第二条:相等的对象必须具有相等的散列码。根据类的equals方法,两个截然不同的实例在逻辑上有可能是相等的,但是根据Object类的hashCode方法,它们仅仅是两个没有任何共同之处的对象。hashCode返回的两个看起来随机的整数,而不是像第二条规定所要求的的那样,返回两个相等的整数。

举一个例子:

package test;

import java.util.HashMap;

class People{
    private String name;
    private int age;
    public People(String name,int age)
    {
        this.age=age;
        this.name=name;
    }
    public void setAge(int age)
    {
        this.age=age;
    }

    @Override
    public boolean equals(Object obj)
    {
        return this.name.equals(((People)obj).name)&&this.age==((People)obj).age;
    }
}
public class Main {
    public static void main(String[] args) {
        People p1=new People("Jack",12);
        System.out.println(p1.hashCode());
        HashMap hashMap=new HashMap();
        hashMap.put(p1,1);
        System.out.println(hashMap.get(new People("Jack",12)));
    }
}

输出结果:

重写了equals方法,认为只要姓名和年龄相等,两个People对象就会被认为同一人。但是输出却为null,这是因为在重写equals方法时忘记重写hashCode方法。hashCode方法是将对象的存储地址进行映射。p1指向的对象和new People("Jack",12)生成的是两个对象,存储地址肯定不同。put方法把一个People对象存放在散列桶中,get方法却在另一个散列桶中查找。

重写hashCode方法:

package test;

import java.util.HashMap;

class People{
    private String name;
    private int age;
    public People(String name,int age)
    {
        this.age=age;
        this.name=name;
    }
    public void setAge(int age)
    {
        this.age=age;
    }

    @Override
    public boolean equals(Object obj)
    {
        return this.name.equals(((People)obj).name)&&this.age==((People)obj).age;
    }

    @Override
    public int hashCode()
    {
        return name.hashCode()*37+age;
    }
}
public class Main {
    public static void main(String[] args) {
        People p1=new People("Jack",12);
        System.out.println(p1.hashCode());
        HashMap hashMap=new HashMap();
        hashMap.put(p1,1);
        System.out.println(hashMap.get(new People("Jack",12)));
    }
}

输出结果为:

参考文献:https://www.jianshu.com/p/e2a3cefe8d40

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值