java--HashMap数据结构

【java--HashMap数据结构】简介
Java为数据结构中的映射定义了一个接口java.util.Map , 此接口主要有四个常用的实现类 , 分别是、、和 , 类继承关系如下图所示:
下面针对各个实现类的特点做一些说明:
(1) :它根据键的值存储数据 , 大多数情况下可以直接定位到它的值 , 因而具有很快的访问速度 , 但遍历顺序却是不确定的 。最多只允许一条记录的键为null , 允许多条记录的值为null 。非线程安全 , 即任一时刻可以有多个线程同时写 , 可能会导致数据的不一致 。如果需要满足线程安全 , 可以用 的方法使具有线程安全的能力 , 或者使用 。
(2) :是遗留类 , 很多映射的常用功能与类似 , 不同的是它承自类 , 并且是线程安全的 , 任一时间只有一个线程能写 , 并发性不如 , 因为引入了分段锁 。不建议在新代码中使用 , 不需要线程安全的场合可以用替换 , 需要线程安全的场合可以用替换 。
(3) :是的一个子类 , 保存了记录的插入顺序 , 在用遍历时 , 先得到的记录肯定是先插入的 , 也可以在构造时带参数 , 按照访问次序排序 。
(4) :实现接口 , 能够把它保存的记录根据键排序 , 默认是按键值的升序排序 , 也可以指定排序的比较器 , 当用遍历时 , 得到的记录是排过序的 。如果使用排序的映射 , 建议使用 。在使用时 , key必须实现接口或者在构造传入自定义的 , 否则会在运行时抛出java.lang.类型的异常 。
对于上述四种Map类型的类 , 要求映射中的key是不可变对象 。不可变对象是该对象在创建后它的哈希值不会被改变 。如果对象的哈希值发生变化 , Map对象很可能就定位不到映射的位置了 。
通过上面的比较 , 我们知道了是Java的Map家族中一个普通成员 , 鉴于它可以满足大多数场景的使用条件 , 所以是使用频度最高的一个 。下文我们主要结合源码 , 从存储结构、常用方法分析、扩容以及安全性等方面深入讲解的工作原理 。
内部实现
搞清楚 , 首先需要知道是什么 , 即它的存储结构-字段;其次弄明白它能干什么 , 即它的功能实现-方法 。下面我们针对这两个方面详细展开讲解 。
存储结构-字段
从结构实现来讲 , 是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的 , 如下如所示 。
这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢?
(1) 从源码可知 , 类中有一个非常重要的字段 , 就是 Node[] table , 即哈希桶数组 , 明显它是一个Node的数组 。我们来看Node[JDK1.8]是何物 。
Node是的一个内部类 , 实现了Map.Entry接口 , 本质是就是一个映射(键值对) 。上图中的每个黑色圆点就是一个Node对象 。
(2) 就是使用哈希表来存储的 。哈希表为解决冲突 , 可以采用开放地址法和链地址法等来解决问题 , Java中采用了链地址法 。链地址法 , 简单来说 , 就是数组加链表的结合 。在每个数组元素上都一个链表结构 , 当数据被Hash后 , 得到数组下标 , 把数据放在对应下标元素的链表上 。例如程序执行下面代码: