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

相关推荐
_GR10 分钟前
每日OJ题_牛客_牛牛冲钻五_模拟_C++_Java
java·数据结构·c++·算法·动态规划
coderWangbuer19 分钟前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
无限大.23 分钟前
c语言200例 067
java·c语言·开发语言
余炜yw24 分钟前
【Java序列化器】Java 中常用序列化器的探索与实践
java·开发语言
攸攸太上25 分钟前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志28 分钟前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
不修×蝙蝠30 分钟前
八大排序--01冒泡排序
java
sky丶Mamba1 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
数据龙傲天1 小时前
1688商品API接口:电商数据自动化的新引擎
java·大数据·sql·mysql
带带老表学爬虫2 小时前
java数据类型转换和注释
java·开发语言