HashMap详解

HashMap

1. 基本介绍

基于哈希表的 Map 接口的实现。

继承结构:

  • java.lang.Object
    • java.util.AbstractMap<K,V>
      • java.util.HashMap<K,V>

内部存储结构

实际是一个链表的数组结构

  1. table数组存放链表的头结点
  2. 该数组的下标索引为(table.length - 1) & hash
  3. 链表过大时,会自动转成树结构(TreeNode),结果类似TreeMap;(Tree bins主要通过hashCode排序,但是如果两个元素继承了Comparable接口可比较,那么就会用compareTo方法比较-反射机制验证)

重要的成员变量

  1. Node<K,V>[] table : 哈希表,第一次使用时初始化;需要时自动调整大小;大小总是2的n次方
  2. int size : 该Map中键值对的数量
  3. int threshold : 阈值,threshold = capacity * loadFactor,当size>=threshold时,HashMap的容量翻倍
  4. int loadFactor : 加载因子

重要的常量

  1. DEFAULT_INITIAL_CAPACITY = 16 : 默认初始容量
  2. MAXIMUM_CAPACITY = 1<< 30 : 带参的构造函数显式声明一个比它大的容量时使用,2的n次方,<= 1<<30
  3. DEFAULT_LOAD_FACTOR = 0.75 : 默认加载因子
  4. TREEIFY_THRESHOLD = 8 : 默认链转树的阈值
  5. UNTREEIFY_THRESHOLD = 6 : 默认树转链的阈值
  6. MIN_TREEIFY_CAPACITY = 64 : 最小链转树的容量,当小于这个值时,采用扩容,而不是链转树的方式降低链的大小

特点:

  1. 允许使用 null 值和 null 键
  2. 不保证映射的顺序,特别是它不保证该顺序恒久不变
  3. 不是同步的

对HashMap如何实现同步

  1. 通过对自然封装该映射的对象进行同步操作
  2. 使用 Collections.synchronizedMap 方法来“包装”该映射
    Map m = Collections.synchronizedMap(new HashMap(...));

性能:

1.影响因素

  1. 初始容量
  2. 加载因子

2.迭代性能

迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。为什么
所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)

2. 初始化过程

1. 初始化参数

  1. initialCapacity
  2. loadFactor

2. 初始化过程

  1. 验证initialCapacity:小于0,抛出异常;大于MAXIMUM_CAPACITY,使用MAXIMUM_CAPACITY作为初始容量。
  2. 验证loadFactor:小于等于0,抛出异常;isNaN,抛出异常(Float.NaN = 0.0f / 0.0f,它与任何其他浮点数不相等,甚至和自己不相等)
  3. 设置loadFactor
  4. 设置阈值threshold = tableSizeFor(initialCapacity)。

NOTES:

  1. 链表数组table未初始化
  2. threshold设置为大于等于initialCapacity的、2的n次方的最小值

3. put过程

  1. 判断hash表是否为空(null或length==0):如果是,resize -> 初始化hash表
  2. 找出key的索引 i = (capacity - 1)& hash(key),判断table[i]是否为null:如果是,在该位置新建链表
  3. 判断key与链表头结点key是否相等,方式:先判断hash值是否相等,再判断==、equals,只要hash相等,并且后面两个条件满足其一,则认为是相同的key值 -> 替换该位置的value(!onlyIfAbsent情况下)
  4. 判断hash表中该节点是否是树节点类型,如果是,putTreeVal
  5. 不是树节点类型,遍历该链表,如果遇到key相等,则替换;链表中没有key相等的,则插入到末尾。binCount(单个链表的长度)大于TREEIFY_THRESHOLD时,尝试链转树,当capacity大于等于MIN_TREEIFY_CAPACITY时,执行链转树,否则,resize
  6. 过程中不是替换操作,更新modCount;put后size>threshold,resize;返回null

4. rehash过程(resize 重建数据结构)

  1. 计算扩容后的容量,临界值。
  2. 将hashMap的临界值修改为扩容后的临界值
  3. 根据扩容后的容量新建数组,然后将hashMap的table的引用指向新数组。
  4. 将旧数组的元素复制到table中。

5. 与其他集合类的比较

与HashTable的区别

  1. 非同步
  2. 允许使用 null作为键值
posted @ 2018-05-08 10:21  走着走着噗噗噗  阅读(162)  评论(0编辑  收藏  举报