白话hashmap常见问题

 

我们都知道,数组的查询效率非常高,而链表的插入效率非常高;那么有没有什么是将他们结合起来使用的呢?当然就是我们要说的hashmap做到了这点。

 

简介

        在JDK1.7版本中hashmap就是通过位桶+链表的形式来实现的。可以理解为首先由一个数组组成,在数组中每个元素下面都要一个链表;这样就是我们1.7版本实现hashmap的方式。到了JDK1.8的时候,加入了红黑树进行实现。

 

一、hashmap实现原理

         当我们往hashmap中加入一个元素的时候,会先调用到put方法,put方法中会去调用putVal方法,我们插入元素的实现内容都在putVal方法中。去计算出key的hash值,用来确认插入到数组中的哪个链表下面,此时如果该链表上没有值就直接插入,如果有值,先判断他们的值是否一致,一致就将value进行替换;不一致去判断是否为红黑树,是红黑树就放入红黑树,不是就插入在链表的尾部,详见下图putVal的流程图。

 (此图转自他出,如有侵权,联系我进行删除)

 

二、加入红黑树之后有什么优缺点呢?

        当我们往hashmap中存入大量的数据的时候 红黑树是一棵接近于平衡的一种二叉树,他的时间复杂度为O(lgn),而链表的时间复杂度达到了O(n),显而易见加入红黑树后提高了我们的查询效率。我们自然要考虑到他是否有什么弊端,加入红黑树有什么缺点呢?每次我们往红黑树中加入元素的时候都很有可能需要对红黑树进行重置,维持再平衡,左旋右旋重新着色等操作;这样就会导致插入变得很麻烦。既然有这样的缺点为什么还要使用红黑树呢?(解释在第三个问题中)

 

三、为什么链表长度达到8转换为红黑树呢?

        根据上面所提出的问题,设计者当然也考虑到了这点,在hashmap源码中有这样一些注解,如下图:

上面的注解通过了解之后发现,他是遵循统计学的泊松分布规律 (exp(-0.5) * pow(0.5, k) ;所得出的插入链表的第1,2,3...个数的几率,我们可以看到当链表的长度超过8插入的几率小于亿分之一,所以其实我们在使用hashmap的时候,真正用到红黑树的情况其实并不多,所以JDK1.8的hashmap的效率也仅仅比1.7中提高了8%到10%。也真是因为在8的时候转换在插入链表的几率如何低,所以选择链表长度达到8的时候将链表转换为红黑树。

 

四、为什么要使用加载因子为0.75?

       加载因子为0.75是什么意思呢?当hashmap的数组填充比(利用率)很大的话,说明利用的空间很多,如果不对数组进行扩容的话,会增加元素碰撞的几率,其中的链表就会越来越长,链表变长就会导致查询效率降低。为什么选择0.75而不是0.6,0.8或者其他呢?通常,加载因子需要在时间和空间成本上寻求一种折中。加载因子过高,例如为1,虽然减少了空间开销,提高了空间利用率,但同时增加的是查询时间成本;加载因子过低,例如0.5,虽然可以减少查询时间成本,但是空间利用率很低;所以加载因子为0.75是一个对空间与时间的折中。这样好像并不能说服为什么一定是0.75呢?还有一个原因,还是要看到上面说的泊松分布那块注解,如下图:

为什么会得出上面所说的插入链表的数超过8的几率达到亿分之一的情况呢?就是建立在加载因子为0.75的前提下。否则如果加载因子过大必然会导致当大量数据插入时,增大链表长度超过8时的概率。

 

五、hash容器为什么一定是2的幂次方

        首先我们存放元素的时候当然最理想的状态是希望元素存放的更均匀一些,能够尽量保证存放在每个链表的几率尽量接近。那么我们就要想办法使得计算出来的hash值能够有机会进入每一个链表。如下代码所示:

static int indexFor(int h, int length) { 

    return h & (length-1);      

}

h是hashCode通过hash算法算出来的值,后面的length-1是数组长度-1,因为长度必须为2的幂次方,所以长度值必然为10000这种形式的,那么将它减一之后就是类似于0111,01111这种除了首位为0,其余位数都为1的形式。又因为需要进行位与运算,与运算只有当上下都为1的时候才为1。如果不是0111这种情况,中间有0出现,类似于00110,00101,01010,01001等这种情况,那么不论h的二进制为什么,在有0的位上面计算出的结果将永远为0,那么就会导致数组上有部分位置将永远不可达,浪费了空间,也增加了元素碰撞的几率,使其他数组下的链表压力增大。所以只有保证数组长度为2的幂次方才能保证有机会进入到数组中的每一个链表。

 

(hashmap中还有许多值得去探讨研究的问题,本人小白一枚,写的不好,理解不深,还望见谅;希望通过这种方式让自己能够对知识进行深刻的学习)

 

 

 

 

 

 

 

本项目是一个基于SSM(Spring+SpringMVC+MyBatis)后端框架与Vue.js前端框架开发的疫情居家办公系统。该系统旨在为居家办公的员工提供一个高效、便捷的工作环境,同时帮助企业更好地管理远程工作流程。项目包含了完整的数据库设计、前后端代码实现以及详细的文档说明,非常适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 系统的核心功能包括用户管理、任务分配、进度跟踪、文件共享和在线沟通等。用户管理模块允许管理员创建和管理用户账户,分配不同的权限。任务分配模块使项目经理能够轻松地分配任务给团队成员,并设置截止日期。进度跟踪模块允许员工实时更新他们的工作状态,确保项目按计划进行。文件共享模块提供了一个安全的平台,让团队成员可以共享和协作处理文档。在线沟通模块则支持即时消息和视频会议,以增强团队之间的沟通效率。 技术栈方面,后端采用了Spring框架来管理业务逻辑,SpringMVC用于构建Web应用程序,MyBatis作为ORM框架简化数据库操作。前端则使用Vue.js来实现动态用户界面,搭配Vue Router进行页面导航,以及Vuex进行状态管理。数据库选用MySQL,确保数据的安全性和可靠性。 该项目不仅提供了一个完整的技术实现示例,还为开发者留下了扩展和改进的空间,可以根据实际需求添加新功能或优化现有功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值