八股文 | hashcode与equals

Java中的hashcode方法

哈希表(也叫散列表)是一种常用的数据结构,通常被用来提高查找效率。无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1)。

//object 方法
public native int hashCode();

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

实际上哈希函数就是从键值到索引的过程,需要通过哈希函数来进行计算。

一. hashCode方法的作用

hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。

Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

//java.util.HashMap的中put方法
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
    	//当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值
        int hash = hash(key.hashCode());
    	//用一个table保存已经存进去的对象的hashcode值
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果存在,则更新value值
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
    	//将新的元素添加到HashMap中
        addEntry(hash, key, value, i);
        return null;
}

hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。

二. equals方法和hashCode方法

对于两个对象,如果调用equals方法得到的结果为true,则两个对象的hashcode值必定相等;

如果equals方法得到的结果为false,则两个对象的hashcode值不一定不同;

如果两个对象的hashcode值不等,则equals方法得到的结果必定为false;

如果两个对象的hashcode值相等,则equals方法得到的结果未知。

import java.util.HashMap;
class People{
    private String name;
    private int age;

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

    public void setAge(int age){
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        return this.name.equals(((People)obj).name) && this.age== ((People)obj).age;
    }

//    @Override
//    public int hashCode() {
//        // TODO Auto-generated method stub
//        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<People, Integer> hashMap = new HashMap<People, Integer>();
        hashMap.put(p1, 1);
        System.out.println(hashMap.get(new People("Jack", 12)));
    }
}

输出结果:

原因:

默认情况下,hashCode方法是将对象的存储地址进行映射。

p1指向的对象和 System.out.println(hashMap.get(new People("Jack", 12)));这句中的new People("Jack", 12)生成的是两个对象,它们的存储地址肯定不同。

//HashMap的get方法的具体实现:
public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
}

所以,在重写equals方法的同时,必须重写hashCode方法

  • 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
  • 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
  • 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
  • 设计hashCode方法和equals方法的时候,如果对象中的数据易变,则最好在equals方法和hashCode方法中不要依赖于该字段。
posted @ 2022-03-25 10:39  张吱吱  阅读(79)  评论(0编辑  收藏  举报