HashMap(JDK1.9)详解
一、HashMap的概念。
1、HashMap类的继承实现关系如下:因此HashMap的功能有:可序列化、可克隆等功能。


2、HashMap的数据结构:数组+链表+红黑树。
3、键值对的存储方案:第一,无冲突时,则存储在数组;第二,有冲突时,且链表长度小于8,则存放在单链表;第三,有冲突时,且链表长度大于8,则存放在红黑树。

二、HashMap中的内部类。
1、数组元素和单链表节点的数据类型是Node类,而红黑树的节点类是TreeNode类。


三、HashMap中的成员变量。

1、容量(capacity),代表该HashMap的数组大小。DEFAULT_INITIAL_CAPACITY表示数组的默认容量,值为16;MAXIMUM_CAPACITY表示容量最大值,值为2的30次方。
2、加载因子(loadFactor),表示数组的使用率。loadFactor表示实际的加载因子;DEFAULT_LOAD_FACTOR表示默认加载因子,值为0.75。
3、扩容阀值(threshold),表示当哈希表的大小大于该扩容阀值时,就会进行扩容,即调用resize方法。扩容阀值 = 实际容量 × 实际加载因子。
4、键值对数量(size),表示实际存储的键值对大小。
5、哈希表(table),代表实际存储的数组。最重要了。
6、树化阀值(TREEIFY_THRESHOLD),表示桶的树化阀值,大小为8,即单链表转换成红黑树的阀值。当单链表长度大于该值8时才会转换。
7、链表还原阀值(UNTREEIFY_THRESHOLD),表示红黑树转换为单链表结构。
8、最小树形化容量阈值(MIN_TREEIFY_CAPACITY)。即当哈希表中的容量 > 该值时,才允许树形化链表 (即将链表转换成红黑树) ,否则,若桶内元素太多时,则直接扩容,而不是树形化 // 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD。转化红黑树的table容量最小容量64(JDK8定义的是64),至少是4*TREEIFY_THRESHOLD(单链表节点个数阀值,用以判断是否需要树形化)=32。用以避免 在扩容和树形结构化的阀值 可能产生的冲突。所以如果小于64必须要扩容。如果在创建HashMap实例时没有给定capacity、loadFactor则默认值分别是16和0.75。
当好多bin被映射到同一个桶时,如果这个桶中bin的数量小于TREEIFY_THRESHOLD当然不会转化成树形结构存储;如果这个桶中bin的数量大于了 TREEIFY_THRESHOLD ,但是capacity小于MIN_TREEIFY_CAPACITY 则依然使用单链表结构进行存储,此时会对HashMap进行扩容;如果capacity大于了MIN_TREEIFY_CAPACITY ,则会进行树化。

public HashMap(int initialCapacity, float loadFactor)
四、HashMap的成员方法。
1、构造方法1。指定容量和加载因子。public HashMap(int initialCapacity, float loadFactor)。先处理入参,后初始化成员变量:加载因子loadFactor和扩容阀值threshold。注意:这里用到了tableSizeFor函数。

2、构造方法2:指定容量。public HashMap(int initialCapacity)。并使用默认加载因子0.75。

3、构造方法3:使用默认容量16和默认加载因子0.75。public HashMap()。

4、构造方法3:public HashMap(Map<? extends K, ? extends V> m)

5、tableSizeFor函数。该函数功能是返回一个比给定整数大且最接近的2的幂次方整数int类型,如给定10,返回2的4次方16。该算法实在精巧。
首先,为什么要对cap做减1操作。int n = cap - 1; 这是为了防止,cap已经是2的幂。如果cap已经是2的幂, 又没有执行这个减1操作,则执行完后面的几条无符号右移操作之后,返回的capacity将是这个cap的2倍。
其次,n与n的无符号右移之后再进行位或运算。当n = 0时,其无符号右移操作后还是为0,然后与0进行或运算,结果为0。
当n为一个正整数时,那么由于n不等于0,则n的二进制表示中总会有一位为1,这时考虑最高位的1。通过无符号右移1位,则将最高位的1右移了1位,再做或操作,使得n的二进制表示中与最高位的1紧邻的右边一位也为1,比如n = 0...1XXXXXX,进行后移之后为00...1XXXXX,然后两者进行或运算之后为:00...11XXXXX。即这是 n |= n >>> 1这句表达的含义。最高位和次高位都为1。(2位1)
进行:n |= n >>> 2的作用,则是将上述结果变为:0...1111XXX。由于经过前面一次操作将最高位和次高位都为1,再右移动两位后再或操作,结果是:最高位和最高位后面的三位都是1,即:0...1111XXX。(4位1)
进行:n |= n >>> 4的作用,则是将上述结果变成:0...1111111。(7位1)。
进行:n |= n >>> 8的作用,则是将上述结果变成:0...1111111。(7位1)。
进行:n |= n >>> 16的作用,则是将上述结果变成:0...1111111。(7位1)。
因此分别进行1位、2位、4位、8位、16位无符号右移再或运算,逐步将最高为的1及后面的所有位数都变成1,结果就是将类似0...1XXX ... XXXX的树变成0...1111 ... 1111。即最大能达到32位1。
最后判断n的值:负数/最大值/正常值+1。

顶顶顶顶顶顶顶

6、hash函数。先获取键对象的hashCode,即哈希码,然后将该哈希码与该哈希码无符号右移16位相异或。因为一个int整数无符号右移16位后,高16位全部为0,低16位被高16位替换。那么相与操作时,因为该哈希码的高16位与0异或就是本身,因此该哈希码的高16位运算之后不会改变,这里运用了一个数与0异或后不会改变的性质。而低16位的结果则是该哈希码的低16位与该哈希码的高16位进行异或运算。这就搅动了低16位的hashCode值。
(1)为何不直接使用key的hashCode码作为存储数组table的下标索引?因为key的hashCode码是int型的32位数值,即约有40亿个不同值,如果让其直接作为数组下标索引而不会出现下标索引越界,那么数组下标的范围就必须达到最大值,即2的30次方,然而这会耗费巨大的空间,正常程序是不会需要这么大的空间的,同时设备也不一定能提供这么大的空间。即需要解决hashCode码与数组大小的匹配关系。
(2)为什么在计算数组下标前需要扰动处理?因为实际上只能根据数组长度length的大小来截取hash码的低length - 1位来作为数组下标的索引?如果直接截取hashcode的低位作为数组下标的索引,那么导致冲突的概率较大,因此使用高16位来扰动低16位将会使hash码值分布更加具有均匀性,数组下标位置更加具有随机性。
(3)为什么采用hash码与运算(数组长度 - 1)?因为,经过前面两步获取到了key的hashCode值,其中高16位不变,而低16位是经过高16位与原低16位进行异或运算的扰动结果,但是还是32位,而数组长度不会那么大,因此需要解决数组下标索引与hash值之间的匹配问题。又因为数组长度length是2的N次方。所以length-1就是低位全部是1,高位全部是0。比如length= 0000 1000 0000,那么length-1就是:0000 0111 1111,那么将length-1与hash值进行与运算来截取hash值的低位作为数组下标索引值。这里运用了与运算来截取位的性质。




7、扩容函数。在插入键值对时,且发现容量不足,则执行扩容操作。两种情况:一是初始化哈希表;二是发现当前数组容量不足。
思路:先保存旧的table,旧的table长度,旧的扩容阀值。



8、put方法。先put,计算出hash,然后调用putVal方法。先判断哈希表为空或长度为0时,则初始化哈希表或更新哈希表容量resize。然后分四种情况插入:
(1)如果该键对象的哈希表索引处为null,则表示该位置没有被使用,即直接插入哈希表数组;
(2)如果哈希表索引处的键对象与插入的键对象相等,则直接更新原来的键对象的值;
(3)如果该键对象的索引处的键的类型为红黑树类型,则插入到红黑树;
(4)如果该键对象的索引处的键的类型为单链表类型,则插入到单链表;
最后更新修改次数modCount,更新键值对数量size,比较size与threshold大小来判断否超过扩容阀值,若超过,则扩容resize。



9、get方法。分四种情况讨论获取的值,一是为null;二是该key对象对应的哈希表索引处就是该键对象,即相等,则直接返回该处的键的值;三是红黑树节点;四是单链表节点。


10、size方法。获取键值对数量。

11、isEmpty方法。判断该hashmap的键值对数量是否为0。

12、containsKey方法。判断该hashmap是否含有该键的键值对。


13、containsValue方法。判断该hashmap是否含有该值的键值对。

14、clear方法。清除哈希表数据。

15、replace方法。替换键值对为新的值,但是键相同。前一个提供原来的键的值,后一个直接用新值替换旧值。


10、remove方法。前一个删除指定键的键值对。后一个删除指定键和值的键值对。


与Hashtable的区别。

分个分隔符个
HashMap(JDK1.9)详解的更多相关文章
- jdk1.8 HashMap扩容原理详解
JDK1.7中,resize时,index取得时,全部采用重新hash的方式进行了.JDK1.8对这个进行了改善. 以前要确定index的时候用的是(e.hash & oldCap-1),是取 ...
- jdk1.7/1.8 HashMap、ConcurrentHashMap详解
摘要: 本文主要参考网上Blog(详见Reference)总结ConcurrentHashMap的各方面知识,方便复习 转自:https://my.oschina.net/hosee/blog/675 ...
- HashTable和HashMap的区别详解(转)
一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的, ...
- HashTable和HashMap的区别详解
一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的, ...
- HashMap源码详解与对比
前几天工作忙得焦头烂额时,同事问了一下关于Map的特性,刹那间懵了一下,紧接着就想起来了一些关于Map的一些知识,因为只要涉及到Collection集合类时,就会谈及Map类,因此理解好Map相关的知 ...
- HashMap底层数据结构详解
一.HashMap底层数据结构 JDK1.7及之前:数组+链表 JDK1.8:数组+链表+红黑树 关于HashMap基本的大家都知道,但是为什么数组的长度必须是2的指数次幂,为什么HashMap的加载 ...
- HashMap源码详解(JDK7版本)
一.内部属性 内部属性源码: //内部数组的默认初始容量,作为hashmap的初始容量,是2的4次方,2的n次方的作用是减少hash冲突 static final int DEFAULT_INITIA ...
- Java HashMap源码详解
Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...
- HashMap resize代码详解(二)
关于其中的resize方法如下: final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = ( ...
- java中list和map详解
一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedList Set下有HashSet ...
随机推荐
- linux常用命令大全2--挂载/dpkg/文件系统分析/apt/光盘/关机
挂载一个文件系统 mount /dev/hda2 /mnt/hda2 挂载一个叫做hda2的盘 - 确定目录 '/ mnt/hda2' 已经存在 umount /dev/hda2 卸载一个叫做hda2 ...
- matlab中画系统零极点的方法
写论文的时候由于需要画出系统的零极点图.但是之前不知道怎么用matlab画,今天研究了一下,拿出来和大家共享.所用到的matlab函数为zplane,matlab给出的解释如下: ZPLANE Z-p ...
- yii---定义全局函数
YII它不像Thinkphp等框架一样,已经有全局函数,YII要使用全局函数需要自己去定义,然后在入口文件中进行引入: 例如:我们看 yii 的入口文件: 看到这里,我们看到有个 autoload.p ...
- yii---进行增删改查
我们使用yii进行数据的增删改查: 一.新增数据 使用model::save()操作进行新增数据 $user= new User; $user->username =$username; $us ...
- 通过IFeatureClass 接口查询 IWorkspace, 查询通配符
IWorkspace pWsI = ((IDataset)pFtCls).Workspace 查询通配符 ISQLSyntax psqls = (ISQLSyntax)(((IDataset)pFtC ...
- [实战]MVC5+EF6+MySql企业网盘实战(2)——用户注册
写在前面 上篇文章简单介绍了项目的结构,这篇文章将实现用户的注册.当然关于漂亮的ui,这在追后再去添加了,先将功能实现.也许代码中有不合适的地方,也只有在之后慢慢去优化了. 系列文章 [EF]vs15 ...
- POJ-1189 钉子和小球(动态规划)
钉子和小球 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7452 Accepted: 2262 Description 有一个 ...
- 启用mapredure历史服务器方法
在mapred-site.xml配置文件中添加如下信息: <property> <name>mapreduce.jobhistory.addres ...
- ArcGIS earth 1.0 beta体验报告——给我一个按钮我将转动整个地球
随着Esri研发中心的ArcGIS earth 1.0 beta版本的全新发布,声势浩大,很多人为之好奇静待观摩其阵容.抽出五分钟体验,良心用户,必得出炉一份体验报告了. -------------- ...
- php.exe
PhpStorm 10.0.2 php interpreter 填入php.exe 暂且不用填写 Listen 63342