HashMap的散列函数

HashMap基础

首先在应用的层次:
大家最常使用的是
Map<xxx,xxx> map = new HashMap<>();
map.put(xxx,xxx);
map.get(xxx);

作为Map类型其特点:键值对,根据其键去取其值。
那么内部HashMap是怎么一个实现机理勒?

HashMap实现机理
  1. 散列函数(哈希函数的一种)
  2. 连续的存储空间(数组)
    大致的流程:

将其键传入哈希函数中,然后映射出相对应的下标,然后将改值存入数组中对应的下标(如果当前位置本身有值就发生了所谓的哈希碰撞)。在查找时将键传入哈希函数,就立刻得出相应的下标,因此HashMap的查找效率为O(1)的原因

什么是散列函数

那么散列函数是什么样的呢?首先散列函数是哈希函数的一种

其实散列函数是可以很简单的!举个例子:
可以根据键字符的ASCII码值进行相加然后在%你的连续存储空间的大小即可!然后每一个键进来之后都可以得到相应的下标;当然这是一个比较简单的散列函数。

散列函数的要求
一、散列函数计算得到的散列值是一个非负整数;
因为最后存储的还是连续的存储空间,那么当然如果当你的哈希函数计算得到的数值时负数的时候,那么自然就会有问题!
二、如果 key1 = key2,那 hash(key1) == hash(key2);
这个也比较好理解,相同的key经过同一个散列函数当然得是同一个下标,除非你的哈希函数加了随机值啥的。当然如果连这个都不满足的话,那大概也不能成为一个哈希函数吧!
三、如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2)。
这个其实并不是哈希函数的必备性质,因为,数据是无穷无尽的。一个程序里面能表示的总是有限的!当不同的键,有相同的值时,就会产生所谓的哈希碰撞!
哈希碰撞
哈希函数得到的最终还是一个下标,然后存储在连续的空间,每一个下标对应着一个位置,那么当不同的键有不同的值的时候,那应该怎么办!覆盖嘛?不可能的!首先解决的方法有很多,但不过这里只说最常用的,那就是链表法!可以想象一下,键对应的下标是一个引用地址,指向一个链表!然后当多个键有相同的哈希值的时候,就可以都存放在该链表上,然后查找的时候,先定位到该链表,在遍历该链表!但不过也是因此,当数据量很大,而连续存储空间又比较小的时候,或者极端情况下,查找的时间复杂度甚至能到O(n)!因此一个好的散列函数就显得非常重要了!
一个好的哈希函数的要求
散列函数不宜太过复杂
如果散列函数太过复杂的话,就会增加每一次添加和查询的时间复杂!因此一个相对来说比较简单的哈希函数尤为重要
散列值要分散
这个如何理解呢?因为如果散列值过于集中就会产生上面所说的情况,太多不同的键有着相同的哈希值,就会导致链表过长,极端情况下查询时间趋近O(n)!而且有些地方则相对空旷造成存储空间乱费!
来看一下java是如何进行的!
 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

首先解释一些h = key.hashCode()这是一个本地方法!native进行修饰的!但不过可以知道h是一个32位的整型变量

其次>>>是位运算符h的二进制向右边推16位然后高位进行无符号补0

^为异或运算!规则是二进制同位上,相同的为0不同的为1!

所以整个式子的意思就是说,使用本地方法得到了key的哈希值,然后在使其和他的低16位进行异或运算!这样计算出来的整型值将“具有”高位和低位的性质,就满足散列值要分散的要求
然后在插入值的时候!

 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);

这里使HashMap的put函数调用的插入操作!最终如何进行插入的!
n为当前HashMap的长度!而hash为上面函数返回的值!可以看到!
p = tab[i == (n-1)&hash] == null!实际上就是上面勒?
首先进行分解(n-1)&hash;依据上文可知,键值,其值存储的位置是其键对应的哈希函数的返回值!然后要进行%存储空间的大小!才能保证能存储进来!这里就是同样的意思!
n-1就是存储空间大小-1为什么要减1呢?首先看一下后面的操作!
&hash,这是什么意思?&是与操作!就是当二进制同位数必须两个都是1才能是1其本质就是求余操作!A % B = A & (B - 1)也就是为了保证其存在于存储空间之内!

if(p = tab[下标] == null)…这句话就代表着,如果当前存储的地方位空!就可以直接创建节点,放入当前位置了!后面的处理当然就是上文说的,当该位置不为空的时候,就是哈希碰撞的时候的解决方案了!这里就不详细展开了!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值