一、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)详解的更多相关文章

  1. jdk1.8 HashMap扩容原理详解

    JDK1.7中,resize时,index取得时,全部采用重新hash的方式进行了.JDK1.8对这个进行了改善. 以前要确定index的时候用的是(e.hash & oldCap-1),是取 ...

  2. jdk1.7/1.8 HashMap、ConcurrentHashMap详解

    摘要: 本文主要参考网上Blog(详见Reference)总结ConcurrentHashMap的各方面知识,方便复习 转自:https://my.oschina.net/hosee/blog/675 ...

  3. HashTable和HashMap的区别详解(转)

    一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的, ...

  4. HashTable和HashMap的区别详解

    一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的, ...

  5. HashMap源码详解与对比

    前几天工作忙得焦头烂额时,同事问了一下关于Map的特性,刹那间懵了一下,紧接着就想起来了一些关于Map的一些知识,因为只要涉及到Collection集合类时,就会谈及Map类,因此理解好Map相关的知 ...

  6. HashMap底层数据结构详解

    一.HashMap底层数据结构 JDK1.7及之前:数组+链表 JDK1.8:数组+链表+红黑树 关于HashMap基本的大家都知道,但是为什么数组的长度必须是2的指数次幂,为什么HashMap的加载 ...

  7. HashMap源码详解(JDK7版本)

    一.内部属性 内部属性源码: //内部数组的默认初始容量,作为hashmap的初始容量,是2的4次方,2的n次方的作用是减少hash冲突 static final int DEFAULT_INITIA ...

  8. Java HashMap源码详解

    Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...

  9. HashMap resize代码详解(二)

    关于其中的resize方法如下: final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = ( ...

  10. java中list和map详解

    一.概叙 List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口, List下有ArrayList,Vector,LinkedList Set下有HashSet ...

随机推荐

  1. sencha touch list tpl 监听组件插件(2013-9-15)

    插件代码 /* *list tpl模版加入按钮监控 *<div class="x-button-normal x-button x-iconalign-center x-layout- ...

  2. Mybatis批量insert报错的解决办法【the right syntax to use near '' at line...】

    Java中使用Mybatis批量插入数据时Mapper.xml中的sql如下: <insert id="batchSave"> into t_emp(emp_name, ...

  3. 安装ubuntu16.04系统后没有无线网络选项的解决方法

    ubuntu系统是自带有无线网络驱动的,因此最好的解决办法是安装是把联网更新选项勾选上,这样在安装是就能自动把无线网络驱动配置好 这是一个比较有效的解决没有无线网络驱动的方法,比后续按网络上的教程自己 ...

  4. 2018牛客网暑期ACM多校训练营(第二场) J - farm - [随机数哈希+二维树状数组]

    题目链接:https://www.nowcoder.com/acm/contest/140/J 时间限制:C/C++ 4秒,其他语言8秒 空间限制:C/C++ 262144K,其他语言524288K ...

  5. Django的URL name 学习

    1.打开工程文件下的url.py: from django.contrib import admin from django.urls import path from django.conf.url ...

  6. linux Service start

    1. crontab的方式 2. 服务的方式.该服务能够持续监测minerd是否在运行,如果没有在运行就会运行minerd:服务也可以做成开机自启动.该服务执行的内容如下,该服务是判断目标服务器的pa ...

  7. 删除server服务文件

    某用户升级ArcGIS for Server后,出现了之前版本server中的服务残留的现象,且服务访问不正常,怎样彻底删除的残留文件. 即怎样删除ArcGIS for Server中发布的某个服务涉 ...

  8. eclipse 64和32位切换

    JAVA_HOME配置的是JAVA_HOME=D:\Java\32\jdk1.6.0_13

  9. sql优化 慢查询分析

    查询速度慢的原因很多,常见如下几种 SQL慢查询分析 转自:https://www.cnblogs.com/firstdream/p/5899383.html 1.没有索引或者没有用到索引(这是查询慢 ...

  10. Python3学习之路~2.6 集合操作

    集合是一个无序的,不重复的数据组合,它的主要作用如下: 去重,把一个列表变成集合,就自动去重了 关系测试,测试两组数据之前的交集.差集.并集等关系 常用操作 >>> list1 = ...