当前位置: 首页 > news >正文

HashMap简介

HashMap 的基本概念

HashMap 是 Java 集合框架中的一种基于哈希表的 Map 实现。它存储键值对(key-value),允许 null 键和 null 值,且不保证元素的顺序。HashMap 通过哈希算法快速定位元素,理想情况下时间复杂度为 O(1)。

HashMap 的核心结构

HashMap 的核心数据结构是数组 + 链表/红黑树。数组的每个元素称为桶(bucket),每个桶存储一个链表或红黑树。当链表长度超过阈值(默认 8)时,链表会转换为红黑树以提高查询效率;当红黑树节点数小于阈值(默认 6)时,会退化为链表。

// 核心字段
transient Node<K,V>[] table; // 存储桶的数组
transient int size; // 键值对数量
int threshold; // 扩容阈值(容量 * 负载因子)
final float loadFactor; // 负载因子(默认 0.75)

HashMap 的哈希计算

HashMap 通过哈希函数将键映射到数组的索引位置。哈希函数的设计目标是尽量均匀分布键值对,减少冲突。

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

哈希计算分为两步:

  1. 调用键的 hashCode() 方法获取哈希值。
  2. 将哈希值的高 16 位与低 16 位异或,以减少哈希冲突。

HashMap 的 put 方法

put 方法是 HashMap 的核心操作之一,用于插入键值对。

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length; // 初始化或扩容if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null); // 桶为空,直接插入else {Node<K,V> e; K k;if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))e = p; // 键已存在else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); // 红黑树插入else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null); // 链表插入if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash); // 链表转红黑树break;}if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))break; // 键已存在p = e;}}if (e != null) { // 更新值V oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;return oldValue;}}++modCount;if (++size > threshold)resize(); // 扩容return null;
}

HashMap 的 resize 方法

resize 方法用于初始化或扩容 HashMap 的桶数组。

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // 双倍扩容}else if (oldThr > 0)newCap = oldThr; // 初始容量设置为阈值else {newCap = DEFAULT_INITIAL_CAPACITY; // 默认初始容量 16newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 默认阈值 12}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);}threshold = newThr;Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap; ++j) { // 迁移数据Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e; // 单个节点直接迁移else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap); // 红黑树迁移else {Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) { // 低位链表if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else { // 高位链表if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}

HashMap 的 get 方法

get 方法用于根据键获取值。

public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;
}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 && ((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;
}

HashMap 的性能优化

HashMap 的性能取决于哈希冲突的频率。以下是一些优化点:

  1. 初始容量和负载因子:根据实际需求设置合理的初始容量和负载因子,减少扩容次数。
  2. 键的哈希码:确保键的 hashCode() 方法分布均匀,减少冲突。
  3. 红黑树:链表长度超过阈值时转换为红黑树,提高查询效率。

HashMap 的线程安全性

HashMap 是非线程安全的。在多线程环境下,可以使用 ConcurrentHashMapCollections.synchronizedMap 实现线程安全。

http://www.dtcms.com/a/274425.html

相关文章:

  • 五星出东方洛老师:gma绘制的洛阳市瀍河回族区的地图和兴趣点
  • 高精加法-P1601 A+B Problem(高精)
  • intellij idea的重命名shift+f6不生效(快捷键被微软输入法占用)
  • 决策树算法在医学影像诊断中的广泛应用
  • 知识科普丨详述agent含义
  • 【深度学习系列】ResNet网络原理与mnist手写数字识别实现
  • 浏览器重绘与重排
  • JAVA ---Excel高效导入(去重1000万数据对比)
  • 聊聊微服务架构中的双token
  • Junit多线程的坑
  • Python爬虫动态IP代理报错全解析:从问题定位到实战优化
  • 【牛客刷题】超级圣诞树(递归法和分形复制法)
  • 实时数仓和离线数仓还分不清楚?看完就懂了
  • SpringCloud 运用(5)—— sentinel限流与seata分布式事务
  • 「备忘」查询日志
  • Spring Boot整合MyBatis+MySQL实战指南(Java 1.8 + 单元测试)
  • 从 JavaFX WebView 迁移至 JxBrowser
  • 【科研绘图系列】R语言绘制系统发育树和柱状图
  • 以科技赋能未来,科聪持续支持青年创新实践 —— 第七届“科聪杯”浙江省大学生智能机器人创意竞赛圆满落幕
  • 宝塔 php支持sqlserver
  • 稀疏激活大模型推理优化:突破效率瓶颈的曙光
  • JavaScript VMP (Virtual Machine Protection) 分析与调试
  • 动态规划初步(完全背包)
  • The 2023 ICPC Asia Hangzhou Regional Contest (H. Sugar Sweet II(基环树,期望))
  • 闲庭信步使用图像验证平台加速FPGA的开发:第九课——图像插值的FPGA实现
  • 【JMeter】执行SQL
  • Elasticsearch 滚动(Scroll)用法、使用场景及与扫描(Scan)的区别
  • Linux 下使用 vim 文本编辑器时的操作指令
  • OGG-00551 ODBC error: SQLSTATE 22007,从字符串转换日期和/或时间时,转换失败
  • 和鲸社区深度学习基础训练营2025年关卡3_Q1(1)