说一说 HashMap 底层数据结构

时间:2022-07-28
本文章向大家介绍说一说 HashMap 底层数据结构,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

答:HashMap 底层是数组 + 链表 + 红黑树的数据结构,数组的主要作用是方便快速查找,时间复杂度是 O(1),默认大小是 16,数组的下标索引是通过 key 的 hashcode 计算出来的,数组元素叫做 Node,当多个 key 的 hashcode 一致,但 key 值不同时,单个 Node 就会转化成链表,链表的查询复杂度是 O(n),当链表的长度大于等于 8 并且数组的大小超过 64 时,链表就会转化成红黑树,红黑树的查询复杂度是 O(log(n)),简单来说,最坏的查询次数相当于红黑树的最大深度。

源码:

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

    private static final long serialVersionUID = 362498820763181265L;

    //初始容量为 16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //最大容量
   static final int MAXIMUM_CAPACITY = 1 << 30;

   //负载因子默认值
   static final float DEFAULT_LOAD_FACTOR = 0.75f;
 
   //桶上的链表长度大于等于8时,链表转化成红黑树
   static final int TREEIFY_THRESHOLD = 8;

   //桶上的红黑树大小小于等于6时,红黑树转化成链表
   static final int UNTREEIFY_THRESHOLD = 6;

   //当数组容量大于 64 时,链表才会转化成红黑树
   static final int MIN_TREEIFY_CAPACITY = 64;

   //记录迭代过程中 HashMap 结构是否发生变化,如果有变化,迭代时会 fail-fast
   transient int modCount;

   //HashMap 的实际大小,可能不准(因为当你拿到这个值的时候,可能又发生了变化)
   transient int size;

   //存放数据的数组
   transient Node<K,V>[] table;

   // 扩容的门槛,有两种情况
   // 如果初始化时,给定数组大小的话,通过 tableSizeFor 方法计算,数组大小永远接近于 2 的幂次方,比如你给定初始化大小 19,实际上初始化大小为 32,为 2 的 5 次方。
   // 如果是通过 resize 方法进行扩容,大小 = 数组容量 * 0.75
   int threshold;

   //链表的节点
   static class Node<K,V> implements Map.Entry<K,V> {
 
   //红黑树的节点
   static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {