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

相关推荐
Victor35642 分钟前
Redis(25)Redis的RDB持久化的优点和缺点是什么?
后端
Victor35643 分钟前
Redis(24)如何配置Redis的持久化?
后端
BD_Marathon5 小时前
【Flink】部署模式
java·数据库·flink
鼠鼠我捏,要死了捏8 小时前
深入解析Java NIO多路复用原理与性能优化实践指南
java·性能优化·nio
胡gh8 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
ningqw8 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
你的人类朋友8 小时前
vi编辑器命令常用操作整理(持续更新)
后端
superlls8 小时前
(Redis)主从哨兵模式与集群模式
java·开发语言·redis
胡gh8 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
言兴8 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试