HashMap 类

1、是 Map 接口使用频率最高的实现类

2、无序(添加、取出顺序不一致)、无索引,不保证映射顺序

3、维护一个哈希表:数组 + 单向链表 + 红黑树

4、key、value 可以是任意引用数据类型

5、key 不允许重复,key 只允许一个 null,value 允许重复,value 允许多个 null

6、添加 key - value,若集合有相同 key,原 value 会被新 value 替换

7、线程不安全,没有 synchronized,没有实现同步互斥

 

底层

1、无参构造器

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

(1)负载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

(2)链表红黑树化及扩容的临界值

static final int TREEIFY_THRESHOLD = 8;

(3)链表红黑树化及扩容的临界值

static final int MIN_TREEIFY_CAPACITY = 64;

(4)红黑树退化为链表的临界值

static final int UNTREEIFY_THRESHOLD = 6;

(5)扩容的临界值

int threshold;

(6)记录哈希表中有效节点数

transient int size;

(7)哈希数组

transient Node<K,V>[] table;

2、添加 key - value、扩容机制

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

(1)得到待添加 key 的 hash 值,散列函数如下,若 key 为 null,hash 值为 0,否则 (h = key.hashCode()) ^ (h >>> 16)

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

(2)若 table 是 null 或大小为 0,第一次扩容至 16

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

(3)根据 key 的 hash 值,算出 key 的索引值:(table.length - 1) & hash,p 指向该索引的位置,即链表的头节点 / 红黑树的根节点

(4)p 为 null,在该索引位置直接创建 Node,添加 key - value

if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

(5)p 不为 null,p.hash 值与待添加 key 的 hash 值相等,且 p.key 与待添加 key 是同一对象 / 待添加 key 不为 null 且和 p.key 经 equals 方法比较后结果一致,则原 key 对应的原 value 被新 value 替换 

if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;

(6)判断 p 是否为红黑树,若 p 是红黑树,就调用 putTreeVal 添加

else if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

(7)若 p 不是红黑树,从第二个 key 进行 for 循环与待添加 key 比较,即重复步骤(5)

else {
    for (int binCount = 0; ; ++binCount) {
        if ((e = p.next) == 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))))
            break;
        p = e;
    }
}

(8)若链表该中的 key 有相同,则原 key 对应的原 value 被新 value 替换;若都不符合,则 key - value 添加到链表尾

(9)添加后,判断该链表是否达到 8 个节点,若链表 >= 8 个节点,调用 treeifyBin 方法,若 table 数组容量 >= 64,将该链表进行红黑树化

(10)若添加 key - value 后,++size(有效节点数)>= threshold = 当前 table 容量 * 0.75(负载因子),则 table 新容量 = 原容量 * 2

if (++size > threshold)
    resize();
posted @   半条咸鱼  阅读(67)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示