JavaSE -- HashMap底层源码分析 - 第一次put

目录

HashMap底层数据结构

存储的基本原理

源码分析

代码

一、进入 HashMap

二、进入 put

二、1. putVal

总结


HashMap底层数据结构

数组 + 链表 + 红黑树

或者直接说 哈希表

存储的基本原理

底层是一个数组(有下标,顺序存储,查询快)。

链表(哈希冲突时,新增的元素挂载到上一个结点下)。

红黑树(链表查询慢,红黑树是一种平衡二叉树,查询效率高)。

源码分析

代码

语句:HashMap<String,String> map = new HashMap<>();

        创建一个HashMap<k,v>类型的集合。

语句:map.put("落花","流水");

        给集合存入数据。 k = "落花" ,v = "流水" 。

一、进入 HashMap

        this.loadFactor 是 HashMap 里的一个属性。

        DEFAULT_LOAD_FACTOR 是一个常量,进入看一眼。

 

        DEFAULT_LOAD_FACTOR -- 默认的加载因子。

        此刻 DEFAULT_LOAD_FACTOR = 0.75f 。f 代表是 float 类型的。

        loadFactor = 0.75f 。

二、进入 put

        前面说过: key = "落花" 。value = "流水" 。

        hash(key) -- 表示计算 key 的哈希值。(可以变相的理解为地址值)

        然后是将 hash(key), key, value, 作为参数传到了 putVal 方法中。

        接下来看一下putVal方法。

二、1. putVal

        有这么一堆东西,咱们慢慢来看。

1、

        这里,hash 是计算的 K 的哈希值,也就是 "落花" 的哈希值。

语句:Node<K,V>[] tab; Node<K,V> p; int n, i;

        这是定义一个初始的 Node 类型的数组 tab 和 p

        定义一个默认为 0 的 n 和 i

        此时:tab = null 。

        p = null 。

        n = 0 。

        i = 0 。

        Node是啥,咱们进去看看。

Node

        咱们发现他是 HashMap 的一个内部类。

        定义的属性有:

        final int hash; -- key的哈希值 ,"落花" 的哈希值 。

        final K key; -- "落花" 。

        V value; -- "流水" 。

         Node<K,V> next; -- 这个是记录下一个数据的节点用的。

        还有好多方法,现在用不到。

        继续往下执行。

记录变量:

1、

tab = null 。

p = null 。

n = 0 。

i = 0 。

2、

语句:if ((tab = table) == null || (n = tab.length) == 0)

                         n = (tab = resize()).length;

        此时 table 不知道是什么,进入看一眼:

        发现 table 还是一个空的 Node 类型的数组。-- 这是一个底层数组

        判断:(tab = table) == null 。成立。里面语句会被执行,但是咱们也看一眼右边是啥意思。(n = tab.length) == 0 。还是成立的。

        执行:n = (tab = resize()).length;

        咱们进入 resize 看一眼:

注意:重点来了,resize是核心代码。

3、

        还是一堆东西,咱一步一步来。

3、1.

语句:Node<K,V>[] oldTab = table;

        前面讲过,table 是个底层数组,空的。

        所以此时:oldTab = null 。

语句:int oldCap = (oldTab == null) ? 0 : oldTab.length;

        三元运算符,括号里的是true,返回冒号前面的值,false,返回冒号后面的值。

        所以此时:oldCap = 0 。

语句:int oldThr = threshold;

        进入 threshold :

        发现 threshold = 0 。

        此时:oldThr = 0 。

语句:int newCap, newThr = 0;

        默认 newCap = 0 。newTer = 0 。

记录变量:

1、

tab = null 。

p = null 。

n = 0 。

i = 0 。

3、1.

oldTab = null 。

oldCap = 0 。

newCap = 0 。

threshold = 0 。

newTer = 0 。

3、2.

语句:if (oldCap > 0) {

        判断 oldCap 是否大于 0 。此时 oldCap = 0 。所以条件不成立,不执行里面语句。

语句:else if (oldThr > 0)

        判断 oldThr 是否大于 0 。此时 oldThr = 0 。所以条件不成立,不执行里面语句。

语句:newCap = DEFAULT_INITIAL_CAPACITY;

        因为上面都不成立,所以必须执行 else 里的语句。

        咱们进入DEFAULT_INITIAL_CAPACITY;看看是什么。

        DEFAULT_INITIAL_CAPACITY 是一个常量。

        1 << 4 。意思是 1 * 2的4次幂 == 16 。

        所以此时 newCap = 16 。

语句:newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

        前面得到:DEFAULT_LOAD_FACTOR = 0.75f 。

        DEFAULT_INITIAL_CAPACITY = 16 。

        16 乘以 0.75 等于 12 。然后强转为 int 类型 。

        所以此时:newThr = 12 。

记录变量:

1、

tab = null 。

p = null 。

n = 0 。

i = 0 。

3、1.

oldTab = null 。

oldCap = 0 。

newCap = 0 。 3、2. 改为 newCap = 16 。

threshold = 0 。

newTer = 0 。 3、2. 改为 newThr = 12 。

3、3.

         判断 newThr 是否等于 0 。条件不成立,所以不执行里面语句。

3、4.

语句:threshold = newThr;

        此时:threshold = 12 。

语句:Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

        这行代码意思是:创建一个新的名为 newTab 的数组,数组长度是newCap。

        也就是说创建了一个 Node 类型的长度为 16 的数组。

语句:table = newTab;

        将 newTab 赋值给 table 这个底层数组。也就是说 table 指向了这个新数组。

后面代码先不看

        最后返回这个新的数组。

记录变量:

1、

tab = null 。

p = null 。

n = 0 。

i = 0 。

3、1.

oldTab = null 。

oldCap = 0 。

newCap = 0 。 3、2. 改为 newCap = 16 。

threshold = 0 。3、4.改为 threshold = 12 。

newTer = 0 。 3、2. 改为 newThr = 12 。

总结

1. 创建HashMap对象时 -- 默认的加载因子 0.75f。

2. 第一次put时,初始化一个Node类型的数组,数组名叫 table。

3.Node是HashMap一个内部类,里面的组成:

        Key 、value、next、hash

4.第一次put时,触发扩容机制resize()。

总结:

        创建一个Node类型的数组,数组长度为16,threshold(阈值) 为12。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值