JDK8util包解读-HashMap(一)关键概念

HashMap 是 Java 集合框架中的一个常用类,实现了基于哈希表的键值对存储和检索功能。它提供了高效的插入、查找和删除操作,并且在大多数场景下具有常数时间复杂度(O(1))。

本文主要对HashMap中一些核心概念进行解释和说明,其他基本概念可以先参考这篇文章:java基础_吃透Java集合系列九:HashMap

关键概念

哈希码(Hash Code)

HashMap 中,每个键都有一个对应的哈希码,它是根据键的 hashCode() 方法生成的。哈希码用于确定键值对在哈希表中的存储位置。 比如其中get方法,是通过Key的Hash方法去找其中的bucket的

java 复制代码
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

hash方法也不是纯粹的key的hashCode,具体如下

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

通过getNode可以了解到hashMap的数据结构是hash数组+链表+红黑树(JDK8下)

java 复制代码
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

哈希表(Hash Table)

HashMap 内部使用哈希表数据结构来存储键值对。哈希表是一个数组,每个元素称为桶(Bucket),通过哈希码将键值对映射到桶的索引位置。哈希表提供了快速的存储和检索操作。

java 复制代码
/**
 * The table, initialized on first use, and resized as
 * necessary. When allocated, length is always a power of two.
 * (We also tolerate length zero in some operations to allow
 * bootstrapping mechanics that are currently not needed.)
 */
transient Node<K,V>[] table;

桶(Bucket)

哈希表中的每个元素称为桶,它可以存储一个或多个键值对。当多个键值对的哈希码相同时,它们会被存储在同一个桶中,形成一个链表或红黑树结构。 对应Node,默认是单向链接数据结构

java 复制代码
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;
    }
}

负载因子(Load Factor)

HashMap 使用负载因子来控制哈希表的填充程度。负载因子是指当前元素数量与哈希表容量的比值,当元素数量超过负载因子和当前容量的乘积时,会触发扩容操作。

链表(Linked List)和红黑树(Red-Black Tree)

在 JDK 8 中的 HashMap 实现中,当链表长度超过阈值时,会将链表转换为红黑树,从而提高在链表较长时的查找效率。

  1. 链表转换为红黑树:

    • 当链表长度达到 TREEIFY_THRESHOLD(默认为 8)时,会将链表转换为红黑树。
    • 转换过程中,首先会创建一个新的红黑树节点作为根节点,并将链表中的元素逐个添加到红黑树中,保持相对顺序不变。
    • 在添加过程中,如果发现链表中的元素已经存在于红黑树中,则将链表元素的值更新到红黑树节点中。
    • 转换完成后,原来的链表将被丢弃,以节省内存。
  2. 红黑树转换为链表:

    • 当红黑树中的节点数量减少到 UNTREEIFY_THRESHOLD(默认为 6)以下时,会将红黑树转换回链表。
    • 转换过程中,首先会将红黑树中的节点逐个遍历,并按照相对顺序添加到链表中。
    • 转换完成后,原来的红黑树将被丢弃,以节省内存。

equals 方法和 hashCode 方法

为了正确存储和检索键值对,键的类必须正确实现 equalshashCode 方法。equals 方法用于判断键是否相等,hashCode 方法用于生成键的哈希码。

参考

一文吃透hashmap的前世与今生
java基础_吃透Java集合系列九:HashMap

相关推荐
我会冲击波13 分钟前
vibe coding 一个多月,我把 Claude Code + Pi 做成了桌面 IDE,还给它移植了 VS Code 的编辑体验
前端·javascript·后端
我是谁的程序员17 分钟前
Windows / Linux / Mac 上不用 Xcode 把 IPA 上传到 App Store,upload 命令详解
后端·ios
凌涘27 分钟前
JWT 登录鉴权 Demo 拆解
后端
JimmtButler33 分钟前
从 Java 到 JS:我终于把闭包想明白了
javascript·后端
带多刺的玫瑰39 分钟前
Leecode#4刷题之寻找两个正序数组的中位数
java·前端·算法
阿无,1 小时前
布隆过滤器
java·算法·哈希算法
掘金者阿豪1 小时前
Codex 怎么突然变慢了?一个需求跑几十分钟,我才发现它的工作方式已经变了
前端·后端
Pocker_Spades_A1 小时前
飞算JavaAI 48分钟能做完Java全栈项目吗?
java·springboot·#ai编程·#飞算javaai·#java代码生成·#aicoding模型·#java开发
Zadig1 小时前
Zadig 全面支持 CRD,至此所有 K8s 资源类型均可一键发布!
后端·devops
得物技术1 小时前
EP-Harness:从个人 AI Coding 到团队级 Agent 工作流|得物技术
后端·程序员·架构