Java之equals和hashcode方法

还记得最初学习java时,老师动不动就眉飞色舞的向我们讲解如何重写equals、hashCode方法,可自己真的一脸懵逼:为什么要重写???

Object源码分别有 equals() 和 hashcode()方法,子类为什么还要重写?

答:1、用于业务上的内容比较,传统的==不适用
         2、提高hash容器的查询效率,
                 如hashmap、hashtable、hashset等
                

 

 

在继续之前,有人会问:==没有提及到,也是相等的意思啊,其实这个很好理解:
1)== 比较的是栈中对象引用的内容,栈中内容对象引用内容其实就是对象在堆中的内存地址
2)A==B,表示A、B共同指向了同一个内存地址,说白了,就是判断两者是否为同一个对象

好了,继续上面的问题,先来看看Object类源码:


原始Object类中,equals和==的作用无区别,都是判断对象的引用(即内存地址)是否相同。


hashcode是根据对象的内存地址,返回对象内存地址的映射值,它是通过底层为native方法(c语言实现),经哈希算法得来的。

再来看看String类:


至于为什么使用了31,请看本文后面解释

而HashMap类是这样:

可以发现:类重写了父类Object的equals和hashCode方法,而且方法实现逻辑也不一样!

为什么需要重写equals方法?

  • Object 中 equals() 比较的是对象是否是同一个对象,而现实业务往往比较的是内容是否相等,而不关心对象是否是同一个
  • 要比较内容是否相等,就需要重写equals();而重写equals()方法,就需要重写它的先决条件判断hashcode()方法。

 

为什么需要重写hashCode方法?

hashcode的一些规定:
1)两个对象相等,hashcode一定相等
2)两个对象不等,hashcode不一定不等
3)hashcode相等,两个对象不一定相等
4)hashcode不等,两个对象一定不等

  • 重写的equal()方法一般比较全面比较复杂,这样效率就比较低,而hashCode只要生成一个hash值进行比较就可以了,效率很高
  • hashCode()既然效率这么高,为什么还要equal()呢?
    因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样,所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:
             1、equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。
             2、hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。


    对于有大量、快速对比的需求,如果都用equal()显然效率太低,所以解决方式是:
    1、先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了)
    2、如果hashCode()相同,再用equal()去对比,如果equal()也相同,则表示这两个对象是真的相同了

    这样既能大大提高了效率也保证了对比的绝对正确性!

    这种大量的并且快速的对象对比,常见用于hash容器,比如:hashset、hashmap、hashtable等等。
    例如:hashset要求里面对象不能重复,那么它内部必然要对新添加进去的每个对象进行对比,
               对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equal()验证,如果hashCode()都不同,    
               则肯定不同,这样对比的效率就很高了。

 

 

 

覆盖equals方法的原则

 

 

素数31与hashcode有什么关系?

String类重写的hashcode方法:
    public int hashCode() {
        int h = hash;
        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;
    }

为什么是31而不是其他数字呢?原因有二:

  • 1)31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。
    质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数

    尽管一些相近的质数,比如37、41、43等等,也都是不错的选择。但31是质子数中一个“不大不小”的存在,

    如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。

    如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,结果会溢出,最终导致数值信息丢失。

    测试结果显示:对超过 50,000 个英文单词进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个,因此这几个数都可以作为生成hashCode值的备选乘数。测试例子请看
  • 2)31可以被 JVM 优化,31 * i = (i << 5) - i
    JVM里最有效的计算方式是位运算:

    * 左移 << :左边的最高位丢弃,右边补全0(把 << 左边的数据*2的移动次幂)。

    * 右移 >> :把>>左边的数据/2的移动次幂。

    * 无符号右移 >>> :无论最高位是0还是1,左边补齐0。   

    所以 : 31 * i = (i << 5) - i(左边 31*2=62,右边 2*2^5-2=62) 两边相等

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值