Java java.util.HashMap实现原理源码分析
2014-08-06 14:46
1091 查看
一、HashMap源码概览
I. 可以结合JDK源码包了解java.util.HashMap实现原理
II. 继承抽象类java.util.AbstractMap,实现了三个接口,分别是:java.util.Map、java.lang.Cloneble、 java.io.Serializable
III. 主要的成员变量
主要成员变量
VI.构造方法
i. 默认总共有四个构造函数,归根到底可以总结只有一个构造,下面将逐一介绍
ii. 构造一:HashMap(int initialCapacity,float loadFactor),源码以及分析如下:
iii.构造二:HashMap(int initialCapacity),源码以及分析如下:
vi. 构造三:HashMap(),源码以及分析如下:
v. 构造四:HashMap(Map<? extends K, ?extends V> m),源码以及分析如下:
vi. 总结:可以看出上述的构造方法,其实归根到底可以总结为,在创建HashMap时,需要两个基本的要素:给定合理的容量、给定合理的负载因子.
二、 HashMap实现具体原理分析
I. 内部存储如图:
II. 根据源码以及上图可以分析下述结论
i. HashMap 内部是以可扩容的数组(Entry[] table)形式来存数数据,每个数组元素是Entry的数据结构为单元的链表
ii. 每个Entry 都要具备基本四个属性:hash值,key值,value值,其在数组中下标位置index,如图
iii. put<K, V>的流程,即添加<K, V>时,HashMap内部的工作流程图如下:
I. 可以结合JDK源码包了解java.util.HashMap实现原理
II. 继承抽象类java.util.AbstractMap,实现了三个接口,分别是:java.util.Map、java.lang.Cloneble、 java.io.Serializable
III. 主要的成员变量
编号 | 变量代码 | 变量注释 |
1 | static final int DEFAULT_INITIAL_CAPACITY = 16; | 默认的初始化容量大小为16,可以通过HashMap(int initialCapacity) 或者HashMap(int initialCapacity,float loadFactor)这两个构造函数来改变此容量大小,但是实际的容量值可能与你设定的值不同(具体操作见下面解释) |
2 | static final int MAXIMUM_CAPACITY = 1 << 30; | 最大的容量值,1左移30位,即2³⁰ |
3 | static final float DEFAULT_LOAD_FACTOR = 0.75f; | 默认的加载因子(作用见下面解释) |
4 | transient Entry[] table; | Entry数组, HashMap内部存储实际就是Entry对象数组(Entry详细解释见下面解释,关键字transient的作用见我另一博客说明, 点击打开链接 ). 此数组如果需要的话,大小可以被重置,但是其长度要一直是2的幂次方 |
5 | transient int size; | 此Map的大小(即k-v映射的长度) |
6 | int threshold; | Map准备扩容的临界阀值(临界阀值的计算方法见下面的解释),即在Map在使用过程中,如果发现Map使用量(即keys大小)已经超过此阀值,则会调用resize()方法进行重置大小(会进行再哈希,将旧数据交换到扩容后的Map容器中,详细步骤见下面解释) |
7 | final float loadFactor; | 加载因子,默认值为此表格中编号3的变量对应的值,即0.75,可以通过构造HashMap(int initialCapacity, float loadFactor) 来改变此值 |
8 | transient volatile int modCount; |
i. 默认总共有四个构造函数,归根到底可以总结只有一个构造,下面将逐一介绍
ii. 构造一:HashMap(int initialCapacity,float loadFactor),源码以及分析如下:
/** * 通过给定的容量值和加载因子来构造一个空的 <tt>HashMap</tt> * 但是指定的这两个参数值的范围是有要求的,具体看下面代码段的分析 * * @param initialCapacity 给定的初始化容量 * @param loadFactor 给定的负载因子 * @throws IllegalArgumentException 如果给定的初始化的容量是负数或者给定的负载因子不是正数 * */ public HashMap(int initialCapacity, float loadFactor) { // 如果给定的初始化容量参数是负数,则会抛出参数不合法的具体异常信息 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); /* * 如果给定的初始化容量大于默认的最大容量值(MAXIMUM_CAPACITY常量见上表格III的编号2变量), * 则实际容量给默认的最大容量值 */ if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; // 如果负载因子不是正数,则会抛出参数不合法的具体异常信息 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); /* * 计算实际容量 * 1.容量值大小要求是2的幂次方. * 2.所以要找一个数字是2的幂次方,且大于等于给定的容量值,则实际的容量可能会跟你设定的容量值不一样 * 3.理解:则譬如你给定的容量值是14,则实际容量值是 2⁴=16, 如果你给定的容量值是16, 则实际容量值也是 2⁴=16 * */ int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; /* * 1.准备扩容的临界阀值计算:实际的容量与负载因子的乘积. * 2.理解:譬如你设置实际容量是16,负载因子是0.75,则当HashMap的使用量大小达到16*0.75=12时, * 则需要调用resize()方法进行扩容,扩容的详细过程见下面解释 */ threshold = (int)(capacity * loadFactor); // 初始化Map Entry元素数组 table = new Entry[capacity]; init(); }
iii.构造二:HashMap(int initialCapacity),源码以及分析如下:
/** * 通过给定的容量值和默认的负载因子(0.75)来构造一个空的 <tt>HashMap</tt> * * @param initialCapacity 给定的初始容量值 * @throws IllegalArgumentException 如果给定的初始化的容量是负数 */ public HashMap(int initialCapacity) { /* * 可以看出这个构造函数只是指定了初始的容量值, * 其实调用的是构造一HashMap(int initialCapacity,float loadFactor)这个构造函数, * 只是负载因子用了默认的0.75(默认的负载因子常量DEFAULT_LOAD_FACTOR) */ this(initialCapacity, DEFAULT_LOAD_FACTOR); }
vi. 构造三:HashMap(),源码以及分析如下:
/** * 通过默认的初始化容量值(16)以及默认的负载因子(0.75)来构造一个空的 <tt>HashMap</tt> */ public HashMap() { // 默认的负载因子常量 this.loadFactor = DEFAULT_LOAD_FACTOR; // 通过默认的初始容量以及默认的负载因子来计算出默认的负载阀值(16 * 0.75 = 12) threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); // 默认的初始容量值 table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); }
v. 构造四:HashMap(Map<? extends K, ?extends V> m),源码以及分析如下:
/** * * 在给定的Map m基础上来构造一个新的<tt>HashMap</tt>,其中给定的Map m的映射数据会被拷贝到新的HashMap中。 * 新构造的HashMap负载因子使用的是默认的0.75,并且其容量值会根据给定的Map m的大小来重新计算容量, * 保证新的HashMap容量充足,具体的计算方法参考下面注释 * * @param m 给定的基础Map,数据会被映射到新的HashMap上 * @throws NullPointerException 如果给定的Map是null,则会抛出空指针异常 */ public HashMap(Map<? extends K, ? extends V> m) { /* *1.可以看出对HashMap的初始化构造还是调用上面的构造一HashMap(int initialCapacity,float loadFactor) *这个构造函数,从而对新建的HashMap进行初始化. *2.首先是根据指定的Map m的大小来重新计算容量,可以看出新创建的的HashMap的容量大小是取 * max(m的大小/默认的负载因子(0.75)的商, 默认的初始容量16) 的值作为初始化容量 *3.负载因子使用是默认的0.75 * */ this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); //将给定的Map m的数据拷贝到新创建的HashMap中 putAllForCreate(m); }
vi. 总结:可以看出上述的构造方法,其实归根到底可以总结为,在创建HashMap时,需要两个基本的要素:给定合理的容量、给定合理的负载因子.
二、 HashMap实现具体原理分析
I. 内部存储如图:
II. 根据源码以及上图可以分析下述结论
i. HashMap 内部是以可扩容的数组(Entry[] table)形式来存数数据,每个数组元素是Entry的数据结构为单元的链表
ii. 每个Entry 都要具备基本四个属性:hash值,key值,value值,其在数组中下标位置index,如图
iii. put<K, V>的流程,即添加<K, V>时,HashMap内部的工作流程图如下:
相关文章推荐
- Java中HashMap底层实现原理(JDK1.8)源码分析
- Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析
- (10) java源码分析 ---- HashMap源码分析 及其 实现原理分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- (10) java源码分析 ---- HashMap源码分析 及其 实现原理分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- JAVA基础之HashMap实现原理及源码分析
- [转]Java HashMap实现原理与源码分析
- 【Java集合学习系列】HashMap实现原理及源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- [置顶] [Java容器]HashMap实现原理和源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- (转载)Java中HashMap底层实现原理(JDK1.8)源码分析
- HashMap实现原理及源码分析
- java并发编程之源码分析ThreadPoolExecutor线程池实现原理
- Java8 HashMap的实现原理分析
- Java集合ArrayList实现原理——源码分析
- Java并发框架Disruptor实现原理与源码分析(三) RingBuffer原理模型与源码分析