带着问题学习?

  1. HashMap 用了什么数据结构?
  1. HashMap 线程安全吗?
  1. 如果 HashMap 存储的数量超过了设置的上限会怎么样?
  1. HashMap 中的负载因子是干嘛的?
  1. HashMap 在使用数组+链表的结构时,新插入的节点是头插入还是尾插入?
  1. HashMap 在使用数组+红黑树结构时,如果扩容,怎么保证树的结构不变?
  1. 在什么情况下,会从 链表 转成 红黑树 存储?
  1. 在什么情况下,会从 红黑树 转成 链表 存储?

介绍

  • HashMap 是 Java 中提供的一种 Map 集合,继承自 AbstractMap,同时实现了 Map 接口
  • 其中 AbstractMap 提供了一套 Map 的基础模板,其中定义了包括 get()、put() 、containsKey() 之类的方法(这也体现了 抽象类是模板,接口是规范 的说法)

源码解析

HashMap 的基本定义:

HashMap 中,默认定义了一下参数,这使得我们可以直接使用 空构造 方法 来创建 HashMap 实例,并且正常使用

HashMap 中数据存储的格式

  • 在 HashMap 中,并不是直接存储我们的键值对的,而是用一个结构 Node,把我们的键值对存放进去,并且在其中存放该节点的 hashCode 和 下一个节点的指针
  • 看看具体定义:

HashMap 求 hashcode 的方法

HashMap 存键值对的过程

  • HashMap 中添加键值对的方法是 put()
  • 看看源码定义

链表转变为树的过程

  • 当哈希冲突链表长度达到阈值时,会将链表转换为红黑树
  • 看源码定义

树退化为链表的过程

  • 当哈希表中的红黑树节点数小于等于设置的 阈值 时,HashMap 会将整个红黑树退化为链表,以节省空间和时间消耗
  • 看看代码定义:

HashMap 中的扩容机制

  • 在 HashMap 中,如果其内的元素个数超过了设置的容量大小,就进行扩容,每次都是翻大 2^m 被
  • 看看代码定义:

问题解答

HashMap 用了什么数据结构?

当单条哈希冲突链表的长度大于 TREEIFY_THRESHOLD 时,就是 数组+红黑树,否则就是 数组+链表

HashMap 线程安全吗?

不安全的,因为在 put 之类的操作时,没有进行相应的同步机制

如果 HashMap 存储的数量超过了设置的上限会怎么样?

  • 如果还没到 Integer.MAX_VALUE,就进行扩容,一般是扩大一倍
  • 如果达到 Integer.MAX_VALUE,就没法扩容

HashMap 中的负载因子是干嘛的?

作为是否扩容的一个决定因子,当哈希表中元素数量达到了负载因子乘以数组容量时,就需要进行扩容操作

HashMap 在使用数组+链表的结构时,新插入的节点是头插入还是尾插入?

尾插

在什么情况下,会从 链表 转成 红黑树 存储?

在单条哈希冲突链表的长度超过 TREEIFY_THRESHOLD 时

在什么情况下,会从 红黑树 转成 链表 存储?

在单个哈希冲突红黑树中的元素个数小于 UNTREEIFY_THRESHOLD 时

推荐/参考

Leetcode_75-颜色分类Jetpack LiveData源码及原理解析