aquels和hashcode详解,通俗易懂,看这篇就够了(含源码)

本文探讨了equals()方法在判断引用类型逻辑相等的重要性,以及如何自定义重写equals以满足业务需求。同时揭示了hashcode()在哈希表中的关键作用,解释了为何重写equals时还需重写hashcode。通过实例和源码分析,揭示了两者在哈希表高效查找中的协作机制。
摘要由CSDN通过智能技术生成

一、equals()

我们都知道==是用来判断两者是否相等。像基础数据类型的话比较当然没有问题,比如:

int a = 1;
int b = 1;
System.out.println(a == b);//true

结果肯定是true,但如果是引用数据类型呢?

        Student s1 = new Student("小李");
        Student s2 = new Student("小李");
        System.out.println(s1 == s2);//false

比如这个代码,我么创建了两个Student对象,我们的需求是同名字的Student需要判断是相同的;但 == 的结果并不是我们需要的,这是因为在引用类型使用 == 时,比较的是两个引用指向的内存地址是否相同,但显然很多时候我们只是需要逻辑上的相等
这时候就需要引入equals来解决。
equals 是 Object类就有的方法,所有的java类都可以重写他,来自定义对象相等的条件。

    public boolean equals(Object obj) {
        return (this == obj);
    }
  • Object类的方法实现,只是用 == 来实现
  • 我们知道我们定义的所有类都自动继承自Object类。

因此,我们可以将自定义类重写equals方法。我们来分析idea自动生成的equals重写方法是怎样的

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;//如果是指向一个对象,肯定相同。
            if (o == null || getClass() != o.getClass()) return false;//如果为空,或者不是本类类型的对象,肯定不相同
            Student student = (Student) o;//将引用强转为本类类型
            return Objects.equals(name, student.name);//自定义判断相等,这里是使用student的name属性来判断。
        }

接下来解答一下可能疑问的点:

  • o.getClass() 就是得到o这个对象的类类型,比如Student类型的对象的类类型就是Class
  • 可以看到最终还是是调用了Objects.equals(name, student.name),相当于比较两个String类型的变量是否相同。实际上这里最终会调用String类里的equals方法。来看看String类equals的重写吧。
    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重写并不复杂,但不可或缺。

二、hashcode()

hashcode()的出现有他特定的作用,首先得先了解一下hash表。
我们知道,数组的链表都有他们各自的缺点。

  • 数组存放,删除元素很麻烦。
  • 链表存放,查找元素很麻烦。
    有没有一个容器可以鱼与熊掌兼得呢?就是Hash表。
    他的内部结构是数组+链表,类似这样,圆形表示存放的元素。
    在这里插入图片描述

如何工作呢?
首先给存放的每一个元素都发一个属于自己的一个hash值。然后根据这个hash值经过计算得到一个下标,然后将元素存放到这个下标下的链表中。

  • 数组的缺点是删除元素,而这里删除元素后并不需要移动其他元素,只需要把对应的结点置空即可。
  • 链表的缺点是查找,hash表将链表分成了很多个很短的链表,所以也不存在这个缺陷。

完美的解决的这两个初始容器的缺陷。

好了。现在我们来看hashcode,他工作在哪一环呢?
我们以HashMap为例,他是java中hash表的具体实现之一,以键-值对存放(key-value)。
如果我们调用put方法存放元素,源码如下:

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

可以看到形参调用了一个hash(),首选我们去putVal()看看这个值有什么用,这里只分析部分源码,因为主要目的是认识hash的作用。

        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

这个操作可以看到 将 (n - 1) & hash 赋给了i,这个hash就是put方法传过来的第一个int形参。下一行可以看出 i 是下标,并且这个下标存放的如果是空,就存放一个元素。看到这已经够了,

  • 我们可以看到hash的一个作用就是配合计算存放元素的下标
  • 并且如果两个元素的hash值是相同的,那么存放的将是同一个下标。
  • 但存放在同一个下标的元素hash值并不一定相同。这就涉及到具体是如何实现的计算。比如这里的(n - 1) & hash,n是容量,当n=16时,hash值为15和31计算出来的下标是一样的。因为 15的二进制1111,31的二进制11111,可以看到计算下标只与低位为准,多余的高位在位运算中并不重要。
    在这里插入图片描述
    现在我们去看看hash()是怎么实现的。
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

可以看到用到了key的hashCode,这句的意思是将hashCode()计算出的值无符号右移16位,再和自己异或,其实就是将这个数的高16位和第16位进行异或。
现在我们来分析一下hashcode()的作用。hash表中需要一个hash值来计算存放的下标,需要逻辑上相等的元素的hash值相等。这里涉及到一个面试题:重写equals()为什么还要重写hashcode()?

  • hash表查找元素时首先通过hash值来算一个下标,然后在同一个下标里的链表中用equals来判断是否元素,主要是为了hash表查找的效率,如果说没有hashcode,怎么判断该存到哪个下标下呢?这就容易出现某一个链表特别长,某些链表特别短,不够均匀,所以hashcode的出现是为了让hash表性能更好。
  • 可以看出重要的是相同的元素的hash值一定要相同。如果相同的元素进入了不同的下标下,那么就会出问题,这就是依靠具体的算法,使得equals()为true的两个元素的hashcode相等。

接下来看看自动生成的hashcode怎么重写的。

        @Override
        public int hashCode() {
            return Objects.hash(name);
        }

调用了Objects类下的hash,注意是Objects,并不是Object。继续跟踪的话,最终调用了Object类下的public native int hashCode();这里就不论述了。涉及到底层的hash算法究竟是怎么实现。

总结:我们需要知道的

  1. equals和==的区别?
    逻辑相等 物理相等
  2. hashcode和equals的关系?
    equals为true的对象,hashcode相等;
    hashcode相等,equals不一定为true。
  3. 他们在hash表中如何互相影响和工作,理解原理过程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值