java HashMap 详解 -- (默认常量和构造函数)

「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

1.前言

HashMap 是我们在java 中用的比较多的一种数据结构,它主要以 key-value 键值对的形式存贮数据,尤其是在查询数据的时候时间复杂度能达到 O(1)。那今天我们就来讲讲HashMap。

2.构造方法

静态常量 含义
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 默认初始化容量:16
static final int MAXIMUM_CAPACITY = 1 << 30 设置的初始化容量最大值 1073741824
static final float DEFAULT_LOAD_FACTOR = 0.75f 默认的负载因子 0.75
static final int TREEIFY_THRESHOLD = 8 桶中bin的数量超过该阈值 由链表转换成树 默认是 8
static final int UNTREEIFY_THRESHOLD = 6 当执行resize操作时,当桶中bin的数量小于该阈值 6时,使用链表来替代树存储
static final int MIN_TREEIFY_CAPACITY = 64 桶中bin 被树化时,最小的hash表容量,默认为 64 。当散列表容量小于该阈值,即使桶中bin的数量超过了 treeify_threshold ,也不会进行树化,只会进行扩容操作。 min_treeify_capacity 至少是 treeify_threshold 的4倍

2.1 new HashMap() 无参构造

图片.png

我们可以看到上面注释中说过 它会创建一个null的hashMap,默认初始化容量 16 ,默认的负载因子0.75 ( 即当前存储的数据量 > 初始容量*负载因子 时就会扩容

2.2 new HashMap(int n)

图片.png

这里面主要调用另外一个有参构造方法,负载因子还是传默认的

2.3 new HashMap(int n, float f)

图片.png

  • 这里首先校验下 initialCapactity ,当它小于0时,抛出异常,大于 MAXIMUM_CAPACITY 时,取MAXIMUM_CAPACITY。
  • 校验负载因子,当负载因子<0,或传入负载因子无效,则抛出异常。
  • 把传入的负载因子赋给 this.loadFactor
  • tableSizeFor(initialCapactity) : 返回给定目标容量大小的2的次方。详解见 2.2.1
  • 把处理后的值 赋给 this.threshold(要调整大小的下一个大小值:capacity * load factor,如果尚未分配表数组,则字段保存初始数组容量,这里就表示初始数组容量

1. tableSizeFor(initialCapactity)

返回给定目标容量大小的2的次方 图片.png 这里为什么 cap -1,如果不减1,当我们传入的cap 是1时,这里会解析成2;当我们传过来的是2,这里会解析成4;当我们传过是 4,就会解析成8,这里使用-1 可以帮我们节省很多空间。

2.4 new HashMap(Map<? extends K, ? extends V> m)

这里直接使用一个Map 创建一个新的 HashMap 图片.png

这里可以看到 负载因子 使用的还是默认的 0.75,然后调用 putMapEntries(m, false) 方法,详解见 2.4.1

1. putMapEntries(Map<? extends K, ? extends V> m, boolean evict)

图片.png

  • 首先它会获取 m的size,如果 s<0 ,则说明 m 为null,不用进行任何处理,下面讨论不为null的情况。
  • table : 表,在第一次使用时初始化,并根据需要调整大小。在分配时,长度总是2的幂。(我们也在一些操作中允许长度为零,以允许当前不需要的自举机制。) 图片.png
  • 如果table 为 null
    • 则用 m的size 除以 负载因子,向上取整 得到 ft
    • 这里把ft转成 int类型;当它大于 MAXIMUM_CAPACITY 取 MAXIMUM_CAPACITY,否则取它本身,这里t实际相当于前面的 initialCapactity。然后调用 tableSizeFor(t) 赋值给 this.threshold
  • 如果 table 不为 null,则说明 this.threshold 有值
    • 判断当 m的size > this.threshold,则说明需要扩容,就调用 resize() 方法,主要作用是:对当前hashMap 中的table 进行初始化或扩容加倍。后续详讲
  • 遍历 m,分别取出它的key 和 Value,放到当前初始化的hashMap 中去,完成初始化。

3.小思考

这里大家可以思考一下,为什么一定要转成 2 的 幂数?

猜你喜欢

转载自juejin.im/post/7035249814094741518
今日推荐