java中的HashMap为什么需要hashcode函数(原理分析)

触发我这次总结的原因

在xxx的一次面试中,面试官的第一道题目就是手写一个HashMap的,因为之前也看过类似的分析,大概知道HashMap的实现原理,但是实际手写一个的时候发现并不是那么容易。

Hash散列算法的原理

Hash散列思想的出现

在《数据结构》中专门有一章节介绍了查找的算法,主要有:顺序查找、折半查找、AVL树查找……,但是这些查找都需要对原来数据结构中的元素进行遍历(无非是在算法上记性了

改进,减少了遍历的次数而已)。后来有人提出能不能建立一种被查找的值与存储位置的映射关系,然后直接可以一步到位呢。基于这种思想,Hash散列的思想出现了。

Hash散列的思想原理

其实Hash散列的原理很简单,就是建立了

location<----->值之间的映射函数f(key),某人在查询一个值的时候,可以直接放到函数f(key)中,直接返回location,然后一个return:

举个例子:

我们现在有很多的地理数据,比如:北京、上海、成都、重庆,利用普通的存储,比如一般的数组,那么存储的方式就是

array=[北京,上海,成都,重庆],比如想查找北京,那只能从第一个元素以此向后比对,如果array[i]的值和我们预期的一样,那就找到了

找到北京需要一次比较,但是找到重庆就需要四次比较了。所以查找的时间复杂度为O(n)

如果利用散列的话,可以考虑这么搞

设计散列函数f(key)={取出第一个汉子的首字母,然后利用首字母在字母表中的序号作为这个key的hans值}

[北京,上海,成都,重庆]  中的每个元素的Hash值为

[2,19,3,3],则数组的第二个位置存放北京,第19个位置存放上海

如果我要查询到上海的值,那么直接通过f(key=‘上海’)=19,直接返回上海的存储位置19,我们直接去19号位置取出来,就是上海的元素。

java中的HashMap的原理

通过上面的Hash散列的原理介绍知道,hash表其实就是实现了映射关系

key<----------->存储位置,但是java中的HashMap稍有区别,那就是java中的HashMap是通过散列之后的hashcode计算location的,不是key直接计算的。

对比一下

普通散列keylocation 
java中HashMapkeyhashcode=hash(key)location=index(hashcode)

 

所以我这边的titile使用的是二次散列,即key值并没有直接转换为location位置,而是先进行了一步散列值计算后,再利用散列值再计算得到的存储位置。

为什么这个费事的需要两步计算才能得到location的值呢,下面看源码分析

源码分析

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

HashMap在进行插入数据的时候(也就是put操作),会首先调用我们自己Key的hashcode方法得到一个hash值,然后再利用这个hashcode的值,再计算得到在内部存储的位置。

现在从源码角度回答一下为什么这样搞

首先从刚才哪个例子(北京、上海哪个例子)中知道,最终location的结果都是int的,因为只有数组才具有随机读取的优点(其他的像链表啊二叉树啊都没有给定下标就直接get的优点)。那么我怎么知道我的HashMap中存的是个啥(Object、String)这些不确定因素怎么才能转成最终的int呢,答案就是java的上帝Object自带了一个hashcode的方法,这个方法的声明就决定了不管谁来,我都能给你一个int的输出,至于输出的int你想怎么搞,那你定。说实话这玩意就是专门给散列用的,也就是为了方便计算location用的。

这样一来问题就解决了。

答案

hashcode函数或者先计算hash值的原因就是为了方便转成int,以方便下一步更好的计算出location的存储位置。

冲突消除

可以简单想一下,相同的key值,hashcode也相同(只针对String,Object的话另说,这也可能是java设计Object的hashcode方法的精妙吧),所以为了避免冲突,先比对下看查询或者put的这个key的hashcode是否相同,不相同那就是没有。如果说是hashcode相同的,那这个时候还的看看key值是不是我要的哪个key,因为不同的key也会产生相同的hashcode。所以才会有了为什么要让“”重写equals方法。

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值