看看HashMap源码理解HashMap的put 方法的执行过程

那我们来看一下源码是怎样实现的

public static void main(String[] args) {
        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put("aaaaa", 10);
    }

Ctrl + B 快捷键跳转

先计算key 的 hash 值

在这里插入图片描述

hash(key)

hash 本质是个数组,为了把元素存进去,就需要把key 值转成数组下标。我们再看源码中是如何计算的。
在这里插入图片描述

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

key与其高16位异或,这样计算目的是为了让最后得到的hash值更加分散,冲突概率降低。在这里hashCode 是Object 类自己的方法,任何一个对象都有这样的方法,也可以重写该方法来实现自己的计算规则。

简单介绍一下哈希表&哈希冲突

哈希表是基于数组的一种存储方式.它主要由哈希函数和数组构成。当要存储一个数据的时候,首先用一个函数计算数据的地址,然后再将数据存进指定地址位置的数组里面。这个函数就是哈希函数,而这个数组就是哈希表。
哈希冲突是指哈希函数算出来的地址被别的元素占用了,也就是,这个位置有人了。好的哈希函数会尽量避免哈希冲突。

初始化

在这里插入图片描述

if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

table 是hash 表中的数组,我们再往里看一下在这里插入图片描述
可以看到是Node实现的,再往里看在这里插入图片描述
在这里Node是链表节点,是处理hash 冲突的一种方式。
链地址法:这种方法的是将所有哈希地址相同的元素i构成一个单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在单链表中进行。链地址法适用于经常进行插入和删除的情况。

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

resize()就是一个扩容操作,将数组长度变长,始终是2^n,看看resize源码如下:
在这里插入图片描述
<< 1就相当于 *2.

根据hash 值找到数组下标,并且把新元素构造成节点插入

三种情况:
a)链表为空
b)链表不为空
c)变成红黑树


///1、链表为空
if ((p = tab[i = (n - 1) & hash]) == null)
// 因为数组产度是2^N  , (n - 1) & hash等价于hash%n
        // 将元素直接插进去
        tab[i] = newNode(hash, key, value, null);
    else {
        //这时就需要链表或红黑树了
        // e是用来查看是不是待插入的元素已经有了,有就替换
        Node<K,V> e; K k;
        // p是存储在当前位置的元素
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p; //要插入的元素就是p,这说明目的是修改值
        // 3、变成红黑树,p是一个树节点
        else if (p instanceof TreeNode)
            // 把节点添加到树中
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            // 2、链表不为空,这时候就是链表结构了,要把待插入元素挂在链尾
            for (int binCount = 0; ; ++binCount) {
                //向后循环
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // 链表比较长,需要树化,
                    // 由于初始即为p.next,所以当插入第8个元素才会树化
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                // 找到了对应元素,就可以停止了
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                // 继续向后
                p = e;
            }

扩容

在这里插入图片描述
针对数组进行扩容,数组越小hash冲突就越严重,效率就越低,进行扩容就很好的提高效率,这里阈值计算的方式和负载因子密切相关。在这里插入图片描述
在这里插入图片描述
就得这张流图很不错就参考过来来源:https://blog.csdn.net/the_one_and_only/article/details/81665098?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159647169419724835817190%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159647169419724835817190&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-2-81665098.first_rank_v2_rank_v25&utm_term=hashmap%E7%9A%84put%E5%8E%9F%E7%90%86&spm=1018.2118.3001.4187

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值