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

HashMap中put方法的执行流程

   在 Java 编程中,HashMap 是一种常用的集合类,它以键值对(Key-Value)的形式存储数据,它具有高效查找、插入和删除的优势。

一.HashMap底层数据结构

  • JDK 1.7:采用 数组 + 链表 的结构。
  • JDK 1.8:优化为 数组 + 链表 + 红黑树。当链表长度超过阈值(默认 8)且数组容量 ≥ 64 时,链表会转换为红黑树,将查询时间复杂度从 O(n)(链表)优化为 O(logn)扩容resize()时若节点数减少到 6 以下,红黑树会退化为链表(避免频繁树结构调整)。

  如图所示:

二.put方法源码分析

put执行流程图:

//1. 调用 putVal 方法并计算哈希值
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}/*该方法通过对 key 的哈希码进行位运算(右移和异或),优化了哈希值的分布,
减少了哈希冲突的概率,为HashMap 中键值对的高效存储和查找奠定了基础。
对 null 键的特殊处理(返回 0)也保证了 HashMap 对null 键的兼容性。*/static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;// 2.在 putVal 中,首先检查 table 是否为空或未初始化。若为真,//  调用 resize() 初始化数组(默认容量 16,负载因子 0.75):(首次put)if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//3.确定键值对存储位置//通过 (n - 1) & hash 计算键值对在数组中的索引 i。若该位置为空,直接创建新节点存入:if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);//4.处理hash冲突else {Node<K,V> e; K k;//4.1覆盖值if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//4.2 若节点是 TreeNode(红黑树),调用 putTreeVal 方法在树中插入或更新键值对else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//4.3 遍历链表,若找到相同 key 则跳出循环(后续覆盖值);若到链表末尾无相同 key,则 //通过尾插法插入新节点。插入后检查链表长度,若 ≥ 8 且数组容量 ≥ 64,调用 //treeifyBin 将链表转为红黑树(若容量不足 64,优先扩容):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 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}//5.若遍历过程中找到匹配 key 的节点(e != null),则覆盖其 value(根据 // onlyIfAbsent标记判断是否覆盖):if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}/* 检查并扩容插入新节点后,size 自增。若 size 超过阈值(capacity × loadFactor),调用 resize() 对数组 进行扩容(容量翻倍):*/++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

总结流程
1.计算哈希:对 key 计算优化后的哈希值。
2.初始化数组:首次 put 时初始化 table。
3.定位存储位置:通过哈希值确定数组索引,无冲突则直接插入。
4.处理冲突:4.1若 key 已存在,覆盖 value。4.2若为红黑树,调用树插入逻辑。4.3若为链表,尾插新节点,满足条件时转红黑树。
5.检查扩容:元素数量超过阈值时,对数组扩容。 

三.HashMap 的基本特性

  • 非线程安全:HashMap 在多线程环境下可能出现数据不一致等问题(如扩容时的死锁风险),若需线程安全,可选用 ConcurrentHashMap 或通过 Collections.synchronizedMap 包装。

  线程不安全示例:

import java.util.HashMap;
import java.util.concurrent.CountDownLatch;/*** 两个线程同时向 HashMap 中插入 1000 个键值对。由于 HashMap 非线程安全,可能出现两个线程同时修改同一位置数据,* 导致最终 map.size()小于 2000(理想情况为 2000),体现了多线程下数据不一致的问题。*/
public class HashMapThreadUnsafeExample {public static void main(String[] args) throws InterruptedException {HashMap<Integer, Integer> map = new HashMap<>();CountDownLatch latch = new CountDownLatch(2);Runnable task = () -> {for (int i = 0; i < 1000; i++) {map.put(i, i);}latch.countDown();};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();latch.await();System.out.println("Map size: " + map.size()); // 可能小于 2000,因线程不安全导致数据覆盖或丢失}
}

  线程安全代码示例1(ConcurrentHashMap)

/*** ConcurrentHashMap 是 Java 并发包(java.util.concurrent)中提供的线程安全的哈希表实现,* 它通过更精细的锁机制(如 JDK 1.8 中的 CAS+ synchronized)来保证线程安全,同时具备较高的并发性能。*/import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {// 直接使用ConcurrentHashMapConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 多线程操作无需额外同步new Thread(() -> {map.put("key1", 1);}).start();new Thread(() -> {int value = map.get("key1");System.out.println("Value: " + value);  //1}).start();}
}

   线程安全代码示例2(synchronizedMap)

/*** 使用 Collections.synchronizedMap 包装通过 Collections.synchronizedMap* 方法将普通 HashMap 包装成线程安全的 Map。该方法返回一个同步的 Map 实现,内部通过* synchronized 关键字保证操作的原子性,但性能相对 ConcurrentHashMap 较差,因为锁的粒度较大。*/import java.util.Collections;
import java.util.HashMap;
import java.util.Map;public class SynchronizedMapExample {public static void main(String[] args) {HashMap<String, Integer> hashMap = new HashMap<>();// 包装成线程安全的MapMap<String, Integer> synchronizedMap = Collections.synchronizedMap(hashMap);// 多线程操作时,需手动同步访问(迭代等操作时)new Thread(() -> {synchronized (synchronizedMap) {synchronizedMap.put("key1", 1);}}).start();new Thread(() -> {synchronized (synchronizedMap) {int value = synchronizedMap.get("key1");System.out.println("Value: " + value); //1}}).start();}
}

  • 允许 null 键值:null 键在 HashMap 中仅有一个(唯一性),null 值则可多个。代码示例:
import java.util.HashMap;public class HashMapNullExample {public static void main(String[] args) {HashMap<Object, Object> map = new HashMap<>();map.put(null, "value1"); // null键map.put("key1", null);   // null值map.put(null, "value2"); // 再次put null键,会覆盖之前的value1System.out.println("Value for null key: " + map.get(null)); // 输出value2System.out.println("Value for key1: " + map.get("key1"));   // 输出null}
}

  • 无序性:HashMap 无法保证键值对的存储顺序,其顺序可能随哈希冲突、扩容等操作动态变化。若需有序,可考虑 LinkedHashMap。 代码示例:
import java.util.HashMap;
import java.util.LinkedHashMap;public class HashMapUnorderedExample {public static void main(String[] args) {HashMap<Integer, String> hashMap = new HashMap<>();hashMap.put(1, "One");hashMap.put(2, "Two");hashMap.put(3, "Three");System.out.println("HashMap order:");hashMap.forEach((k, v) -> System.out.println(k + ": " + v));LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();linkedHashMap.put(1, "One");linkedHashMap.put(2, "Two");linkedHashMap.put(3, "Three");System.out.println("\nLinkedHashMap order:");linkedHashMap.forEach((k, v) -> System.out.println(k + ": " + v));}
}

相关文章:

  • IOS微信小程序无法显示背景图片
  • 音频识别优化(FFT)
  • 认识Vue
  • Java锁的分类与解析
  • QT6 源(34):随机数生成器类 QRandomGenerator 的源码阅读
  • 科学护理进行性核上性麻痹,缓解病痛提升生活质量
  • 用cython将python程序打包成C++动态库(windows+Vistual Studio2017平台)
  • Lombok @Builder 注解的进阶玩法:自定义 Getter/Setter 方法全攻略
  • 在没有第三方库的情况下使用 Python 自带函数解码
  • 3.串口通信之SPI
  • Java学习手册:Java内存模型
  • 22、字节与字符的概念以及二者有什么区别?
  • 【Python爬虫基础篇】--1.基础概念
  • MCP Server和Client的基本使用方法
  • halcon模板匹配(八)alignment_for_ocr_in_semiconductor
  • OCR:开启文档抽取的智能变革之门
  • 4.16 AT好题选做
  • 探索关系型数据库 MySQL
  • LFI to RCE
  • DiffuRec: A Diffusion Model for Sequential Recommendation
  • 特朗普声称中方领导人打了电话,外交部:近期中美元首没有通话
  • 人社部:将会同更多部门分行业、分领域制定专项培训计划
  • 李公明|一周画记:哈佛打响第一枪
  • 一周观展|五一假期将到,特展大展陆续开幕
  • 2025年度人大立法工作计划将公布:研究启动法律清理工作
  • 李公明︱一周书记:大学的价值、韧性以及……不相称的对抗