jdk1.8 HashMap 实现 数组+链表/红黑树

转载至 http://www.cnblogs.com/leesf456/p/5242233.html

一、前言

  在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化,其中最重要的一个优化就是桶中的元素不再唯一按照链表组合,也可以使用红黑树进行存储,总之,目标只有一个,那就是在安全和功能性完备的情况下让其速度更快,提升性能。好~下面就开始分析源码。

二、HashMap数据结构

  

  说明:上图很形象的展示了HashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树的引入是为了提高效率。所以可见,在分析源码的时候我们不知不觉就温习了数据结构的知识点,一举两得。

三、HashMap源码分析

  3.1 类的继承关系 

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

  可以看到HashMap继承自父类(AbstractMap),实现了Map、Cloneable、Serializable接口。其中,Map接口定义了一组通用的操作;Cloneable接口则表示可以进行拷贝,在HashMap中,实现的是浅层次拷贝,即对拷贝对象的改变会影响被拷贝的对象;Serializable接口表示HashMap实现了序列化,即可以将HashMap对象保存至本地,之后可以恢复状态。

  3.2 类的属性 

 

  说明:类的数据成员很重要,以上也解释得很详细了,其中有一个参数MIN_TREEIFY_CAPACITY,笔者暂时还不是太清楚,有读者知道的话欢迎指导。

  3.3 类的构造函数

  1. HashMap(int, float)型构造函数

 

  说明:tableSizeFor(initialCapacity)返回大于initialCapacity的最小的二次幂数值。

 

  说明:>>> 操作符表示无符号右移,高位取0。

  2. HashMap(int)型构造函数。

 

  3. HashMap()型构造函数。

 

  4. HashMap(Map<? extends K>)型构造函数。

 

  说明:putMapEntries(Map<? extends K, ? extends V> m, boolean evict)函数将m的所有元素存入本HashMap实例中。 

 

  3.4 重要函数分析

  1. putVal函数  

 

  说明:HashMap并没有直接提供putVal接口给用户调用,而是提供的put函数,而put函数就是通过putVal来插入元素的。

  2. getNode函数

 

  说明:HashMap并没有直接提供getNode接口给用户调用,而是提供的get函数,而get函数就是通过getNode来取得元素的。

  3. resize函数  

 

  说明:进行扩容,会伴随着一次重新hash分配,并且会遍历hash表中所有的元素,是非常耗时的。在编写程序中,要尽量避免resize。

  在resize前和resize后的元素布局如下

  说明:上图只是针对了数组下标为2的桶中的各个元素在扩容后的分配布局,其他各个桶中的元素布局可以以此类推。

四、针对HashMap的思考

  4.1. 关于扩容的思考

  从putVal源代码中我们可以知道,当插入一个元素的时候size就加1,若size大于threshold的时候,就会进行扩容。假设我们的capacity大小为32,loadFator为0.75,则threshold为24 = 32 * 0.75,此时,插入了25个元素,并且插入的这25个元素都在同一个桶中,桶中的数据结构为红黑树,则还有31个桶是空的,也会进行扩容处理,其实,此时,还有31个桶是空的,好像似乎不需要进行扩容处理,但是是需要扩容处理的,因为此时我们的capacity大小可能不适当。我们前面知道,扩容处理会遍历所有的元素,时间复杂度很高;前面我们还知道,经过一次扩容处理后,元素会更加均匀的分布在各个桶中,会提升访问效率。所以,说尽量避免进行扩容处理,也就意味着,遍历元素所带来的坏处大于元素在桶中均匀分布所带来的好处。如果有读者有不同意见,也欢迎讨论~

时间: 2024-11-08 19:13:55

jdk1.8 HashMap 实现 数组+链表/红黑树的相关文章

Java 1.8 HashMap源码解析 桶数组+单链表+红黑树

1 // 非线程安全 2 // 继承了AbstractMap 3 // 实现了Map.Cloneable.Serializable接口 4 // 后面2个接口是标记接口,没有抽象方法. 5 // 表示HashMap可以浅复制.序列化和反序列化. 6 public class HashMap<K,V> extends AbstractMap<K,V> 7 implements Map<K,V>, Cloneable, Serializable 1 // 序列化版本唯一标识

JDK1.8 HashMap为什么在链表长度为8的时候转红黑树,为啥不能是9是10

起因: 这个问题是在面试某公司的时候面试官提的问题,当时没回答上来.归根到底还是因为自己复习基础的时候还不够仔细,也缺乏思考. 首先 我觉得需要确认一下,是不是随便什么情况下只要满足了链表长度为8就转红黑树呢?答案自然不是,为什么不是,看代码: /** * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. */ f

JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程

关于HashMap的扩容过程,请参考源码或百度. 我想记录的是1.8 HashMap扩容是对链表中节点的Hash计算分析. 对术语先明确一下: hash计算指的确定节点在table[index]中的链表位置index,不是节点的hash值. 1 Node<K,V> loHead = null, loTail = null; //这两个是记录重新hash计算后仍在原位置(设为index)的节点 2 Node<K,V> hiHead = null, hiTail = null; //这

JDK1.8 HashMap源码分析

一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低.而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间. 下图中代表jdk1.8之前的hashmap结构,左边部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解

JDK1.8 HashMap中put源码分析

一.存储结构      在JDK1.8之前,HashMap采用桶+链表实现,本质就是采用数组+单向链表组合型的数据结构.它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置.HashMap通过key的hashCode来计算hash值,不同的hash值就存在数组中不同的位置,当多个元素的hash值相同时(所谓hash冲突),就采用链表将它们串联起来(链表解决冲突),放置在该hash值所对应的数组位置上.结构图如下:     图中,紫色部分代表哈希表,也称为哈希数组,数组中每个元素

jdk1.8 HashMap源码讲解

1. 开篇名义 jdk1.8中hashMap发生了一些改变,在之前的版本中hsahMap的组成是数组+链表的形式体现,而在1.8中则改为数组+链表+红黑树的形式实现,通过下面两张图来对比一下二者的不同.                   jdk1.8之前的hashMap结构图,基本对象为Entry<k,v>                               jdk1.8的hashMap结构图,基本对象改为了Node<k,v> 注意:无论Entry<key,valu

jdk1.8源码解析:HashMap底层数据结构之链表转红黑树的具体时机

前言 本文从三个部分去探究HashMap的链表转红黑树的具体时机: 一.从HashMap中有关“链表转红黑树”阈值的声明: 二.[重点]解析HashMap.put(K key, V value)的源码: 三.测试: 一.从HashMap中有关“链表转红黑树”阈值的声明,简单了解HashMap的链表转红黑树的时机 在 jdk1.8 HashMap底层数据结构:散列表+链表+红黑树(图解+源码)的 “四.问题探究”中,我有稍微提到过散列表后面跟什么数据结构是怎么确定的: HashMap中有关“链表转

HashMap底层数据结构之链表转红黑树的具体时机

前言 本文从三个部分去探究HashMap的链表转红黑树的具体时机: 1.从HashMap中有关"链表转红黑树"阈值的声明:2.[重点]解析HashMap.put(K key, V value)的源码:3.测试: 一.从HashMap中有关"链表转红黑树"阈值的声明,简单了解HashMap的链表转红黑树的时机 HashMap中有关"链表转红黑树"阈值的声明: /** * 使用红黑树(而不是链表)来存放元素.当向至少具有这么多节点的链表再添加元素时,

红黑树你搞懂了没

红黑树的定义比较简单,无非是在插入和删除的过程中自平衡规则多了一些,不过再多也只是个位数而已 Linux虚拟内存管理,Java中的TreeMap和TreeSet,以及JDK1.8之后的HashMap也有用到红黑树数据结构 红黑树是一种 自平衡 的二叉树,所谓的自平衡是指在插入和删除的过程中,红黑树会采取一定的策略对树的组织形式进行调整,以尽可能的减少树的高度,从而节省查找的时间. 红黑树的特性如下: 1.结点是红色或黑色2.根结点始终是黑色3.叶子结点(NIL 结点)都是黑色4.红色结点的两个直