HashMap原理+源码初探

HashMap原理

HashMap是由 对象数组+链表 构成

通过 对象的hashcode值,对数组长度取余 得到 下标值

如: 数组长度是 16;一个hashcode值是17, 另一个hashcode值是33 对16取余 都为1;就需要都放入第一个哈希桶中。这时都要存放在1下标这个位置 就要使用链表,一个个连起来。

jdk8版本,进行了优化,当这个哈希桶已经存储七个了对象了, 到达8的时候 -> 由链表变为红黑树。

即 哈希桶中的数据量大于8时,从链表转换为红黑二叉树;当哈希桶中的数据量减少到6时,从红黑树转换为链表。

注意 :已知哈希桶的数据量为7,这是桶容量变为6时 ,一定会从红黑树转为链表吗?

​ 不一定,数据量为7的时候,还不知道是否到达过8;

HashMap默认的值:

  • 初始桶的数量:16

  • 散列因子:0.75

  • 当 哈希桶75%都有数据了,这时候就要对桶进行扩容

  • 扩容大小就是原长度的两倍

在这里插入图片描述

源码分析

  1. 调用put(key,value) 使用hash算法计算存储的位置
  2. 如果当前Node数组table是null,或者长度为0 ,就调用resize()方法进行扩容(默认的初始容量和默认的加载因子),再进行插入到table去; 如果不为空 进入第3步
  3. 判断当前table[i](第一步计算出来的位置) 是否有元素 ,如没有元素就直接将新的node节点 插入;如有,进入第4步
  4. 判断 table[i] 的第一个位置上的key值是否与 传入的key相等,如果相等,则用新值覆盖旧值;如果不相等则进行第5步
  5. 判断table[i] 是否是treeNode,如果是treeNode,则用 putTreeVal() 方法进行插入(相同的key,旧值覆盖新值;不相同就直接插入);如果不是treeNode 而是 链表,则进入第6步;
  6. 遍历链表,如果链表长度大于等于8了 则转为putTreeVal()进行插入 , 不然就继续链表插入,如果有相同的key就把旧值覆盖成新值。
  7. 插入成功就将 modCount++,如果当前size大于HashMap规定的临界值,则进行table扩容

在这里插入图片描述

初始容量 和 默认加载因子:

在这里插入图片描述

找到数据添加put方法

在这里插入图片描述

存储数据的 node 节点 是由一个 hash值,key,value ,还有个next指针构成

/**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }

    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

put()方法 调用了putVal()方法

在这里插入图片描述

hash() : 根据key 来 计算hash值

在这里插入图片描述

putval() 方法源码分析

/**
     * Implements Map.put and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0) // table为桶,判断桶是否是null,再判断长度是否为0
        // 调用 resize() 进行扩容
        n = (tab = resize()).length; // tab是哈希表,n就是数组的长度
    if ((p = tab[i = (n - 1) & hash]) == null) // n是数组容量 n-1(length) 与 hash 进行取余
        // 如果该位置是null 就直接 存入 hash表tab
        tab[i] = newNode(hash, key, value, null);
    else { // 桶不是null;
        HashMap.Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            // 这个判断是hash桶的第一个元素的key与 p的key 相同
            // 判断这个 key 完全相同
            e = p; // 将p 赋给 e;
        else if (p instanceof HashMap.TreeNode) // 判断是否是树根(哈希桶数量有8个了)
            // 如果有相同的 就一样将 e覆盖;
            e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else { // 这个分支表示是链表 (数据<8)
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) { // e指向 p的next节点  如果是null 就说明没有相同的节点 然后加入到链表中,
                    // 有值了!!! 但是e还是为null 太妙了
                    p.next = newNode(hash, key, value, null);
                    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)))) // 如果是重复的 之前就赋予了p的next节点这时e就不是null了
                    break;
                p = e; // p向后移动;
            }
        }
        if (e != null) { // existing mapping for key 如果 key相同 e就不是null
            // 新的值 覆盖 旧节点的值
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // 增加容量
    ++modCount;
    if (++size > threshold)
        // 长度不够进行扩容
        resize();
    afterNodeInsertion(evict);
    return null;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值