zoukankan      html  css  js  c++  java
  • Java连载152HashMap中的hash函数有什么用 心悦君兮君不知

    一、取模运算和取余运算

    • 取余运算,这个很好理解,我们经过多年的数学学习也知道,就是求余数,一个整数和另一个整数相除,得到它们的余数,就是我们说的取余
    • 取模运算,通俗的来讲大多运算在计算机领域,取模运算其实就是两个二进制数字之间做与运算,它们最后得到的数字就是取模
    • 我们举个简单的例子,有一个二进制数字0000 0001 1001 1101,1111 0101 1010 0011,这个两个数字做与运算,它们相同位置的数字,如果有一个数字出现1,那么计算后的数字的那个位置就是1,这两个数字与运算后的值为1111 0101 1011 1111这就是取模运算

    二、hash函数在HashMap的源码

        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    • 我们乍一看这个东西很懵,到底有什么用,讲这个源码之前,我们得先知道hashCode()函数是干嘛的?我们先看源码
        @HotSpotIntrinsicCandidate
        public native int hashCode();
    • 这个就是hashCode函数的实现,从native关键字可以看出这是一个由底层C代码实现的函数,因此我们不必知道它是怎么实现,我们只需要知道,它就是返回一个int类型的数字,这个数字就相当于对象的身份证,指纹,因此可以利用这个“身份证”来进行归类,我们知道int值域在-2147483648 到 2147483648,这个范围有40多亿,因此想要相同也不容易,但是我们的HashMap底层数据结构是一个数组啊,用于存储这些节点,初始大小为16,因此按照我们常规做法就是,用这个“身份证”和数组大小(也就是16)做取余运算,这样我们就是把一个很大的数字映射成一个16以内的整数。然后我们就可以把这个节点存储到这个数组的某一个位置了。

    三、hashMap中映射的解决方案

    • 这个解决方案其实很好,但是我们还没有其他的解决方案呢?
    • 其实是有的,在hashmap中的做映射,使用是取模运算而不是取余运算,这是为啥?
    • 因为取余运算的效率远高于取余运算,我们知道任何数据存储在计算机内部都是以二进制形式存储的,因为两个int整数进行取模运算的时候,做几次比较立即就得出了,但是取余就要运算很长
    • hashMap内部使用取模,比如我们一个对象的“身份证”是
    • 1010 0111 0100 1010 1111 1001 0010 1010
    • 我们数组大小是16,16的二进制是0000 0000 0000 0000 0000 0000 0001 0000
    • 我们减一,那就是 0000 0000 0000 0000 0000 0000 0000 1111,用这个数字和上面那个“身份证”取模得到0000 * 7个,最后四位就是1010,多好啊!把一个数字映射成了一个16以内的数字,我们看看源码是不是这个方法
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict)
     
    {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
    /// 后面省略了
    • 可以看见正好n-1就是16-1=15这样2进制最后4位都是1,就是模取出来了
    • 这也正好解释了为什么扩容都是*2了,因为-1之后就是后面几位数字都是1了,便于好取模,求出映射
    • 回到我们刚才说的hash函数有什么用
    • 我们看这里h&(h>>16),其实就是身份证和身份证往右移动16位之后取模,我们知道一个int值类型就是一个32位的二进制,这样其实就是把前16和后16位进行取模,这样就把一个增大了随机性,因为你看原来的方法只取了后面4位,前面的28位都没啥用,用这个方法之后,再取后4位就是把前面的16位也拿来了
    • 总结:hash函数就是为了是对象的hash值增加随机性,它集合前16位和后16的特征进行计算,这样再被取模之后,会让对象在HashMap中分布更均匀。

    四、源码:

  • 相关阅读:
    Asp.Net根据角色验证
    牛客登录(四)
    外键约束
    update 和replace更新表
    每日一题力扣485
    牛客登录(6)开窗函数
    牛客登录(5)
    MySQL的UPDATE或DELETE中子查询不能为同一张表
    牛客登录(二)
    剑指offer:二分
  • 原文地址:https://www.cnblogs.com/ruigege0000/p/15700598.html
Copyright © 2011-2022 走看看