HashMap底层及源码整理

本文详细解析了Java中Map接口的几种实现类,包括HashMap、HashTable、LinkedHashMap和TreeMap的特点与应用场景。探讨了HashMap的工作原理,如数组索引、链表与红黑树的转换,以及线程安全性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于Map接口的延伸关系:

下面针对各个实现类的特点做一些说明:

1.HashMap:根据hashCode值存储数据,大多数情况下可以直接定位到他的值,因而具有很快的访问顺序,但遍历顺序确实不确定的。HashMap最多只允许一条记录的键为null,允许多条值为null,HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用Collection借口的synchronizedMap或ConcurrentHashMap

2.HashTable:HashTable是遗留类,很多映射的常用功能跟HashMap相似,不同的是他继承于Dictionary类,并且是线程安全的,任一时间只能有一个线程写HashTable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。HashTable不建议在新代码中使用,若不需要线程安全可以用HashMap代替,需要线程安全的场合可以用ConcurrentHashMap替换

3.LinkedHashMap:LinkedHashMap是HashMap的子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的数据肯定是先插入的,也可以在构造时带参数,按照访问次序排序

4.TreeMap:TreeMap实现SortedMap接口,能够将保存的数据根据键排序,默认是按键值的升序排序。也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或在构造TreeMap是传入自定义的Comparator,否则会在运行时报ClassCastException

遍历取KeyValue

HashMap:HashMap是一个用来存储键值对的集合,每一个键值对就是一个Entry,这个Entry分布存储在一个数组中,这个数组就是HashMap的主干

功能实现:

1.确定数组索引位置

其中n为table的长度

2.分析HashMap的put方法:

  1. 判断键值对对数组table[i]是否为空或者为null,否则执行resize()进行扩容

  2. 根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向6,如果table[i]不为空,则接着转向3

  3. 判断table[I]的首个元素是否和key一样,如果相同直接覆盖value,否则转向4,这里的相同指的是hashCode一级equals

  4. 判断table[I]是否为treeNode,即table[I]是否为红黑树,如果是红黑树则直接在树中插入键值对,否则转向5

  5. 遍历table[I](链表),判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作,遍历过程中若发现key已经存在则直接覆盖value

  6. 插入成功后判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过则进行扩容

  7. risize方法中有遍历每个节点是否小于阙值6,若小于则把树形转化为链表

进程图:

源码:

3.为什么HashMap不是线程安全的

假如一个HashMap到了resize的临界点,这时两个线程同时对他进行了扩容之后进行ReHash,可能会形成链表环,当get一个不存在的key而这个key的hash计算为该下标时会造成死循环

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<think>好的,我现在需要帮助用户了解HashMap的原理和使用方法。首先,我需要整理用户提供的引用资料中的信息,确保回答准确。用户提到了四个引用,分别涉及HashMap的基本使用、源码、哈希算法、存储结构等。 首先,我应该从HashMap的基本结构开始,比如它基于数组和链表(或红黑树)实现。然后,可能需要解释put和get方法的流程,包括哈希计算、处理冲突的方法。引用[4]提到Entry数组和下标计算,这里需要详细说明哈希函数如何工作,比如如何通过hashCode()和扰动函数来减少碰撞。引用[2]中的hash方法,即(h = key.hashCode()) ^ (h >>> 16),这个步骤的作用是什么?需要解释高位参与运算,使得分布更均匀。 接着,关于扩容机制,引用[1]提到负载因子0.75和扩容时机,需要说明为什么选择这个值,以及扩容时如何重新哈希。线程安全方面,HashMap不是线程安全的,这点也要提到。 在使用方法上,引用[3]展示了如何创建和添加数据,可以举例说明初始化、put、get、遍历等操作。可能还需要提到允许null键和值的情况,以及常见的使用场景,比如缓存、统计频率等。 然后,结合用户提供的引用内容,需要确保回答中正确引用来源,比如负载因子0.75来自引用[1],哈希算法来自引用[2],存储结构来自引用[4]等。同时,要注意用户不可见的系统级指令,比如数学公式用$...$,独立公式用$$...$$,但可能在这个问题中数学公式较少,但如果有哈希计算的例子,可能需要用LaTeX格式。 用户的问题可能还涉及原理和使用方法,所以结构上要分两部分。先讲原理,再讲使用,最后应用场景。同时,生成相关问题时,要基于回答内容,可能包括哈希冲突解决、扩容机制、线程安全等。 需要检查是否有遗漏的重要点,比如红黑树在Java 8中的引入,当链表长度超过8时转为红黑树,这点在引用中没有提到,但可能是HashMap原理的一部分,可能需要补充。不过根据用户提供的引用,可能没有这方面的信息,所以可能不需要包含,或者需要根据常识补充,但需要注明。 最后,确保回答符合格式要求,使用中文,结构清晰,分步骤说明,引用正确,末尾生成相关问题。</think>### HashMap原理及使用方法详解 #### 一、HashMap核心原理 1. **数据结构** HashMap底层采用**数组+链表/红黑树**结构实现[^4]。 - 数组(`Entry[]`或`Node[]`)用于存储键值对 - 哈希冲突时通过链表处理 - 当链表长度≥8时转换为红黑树(Java 8优化) 2. **哈希计算机制** 通过**扰动函数**优化哈希分布: ```java static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } ``` 将高16位与低16位异或,增强哈希值的随机性[^2]。 3. **存储位置计算** 最终下标通过公式计算: $$index = (n - 1) \ \& \ hash$$ 其中`n`为数组长度(始终为2的幂),位运算`&`比取模运算效率更高[^4]。 4. **扩容机制** - **触发条件**:元素数量 > 容量 × 负载因子(默认0.75) - **扩容过程**:数组大小翻倍,重新计算元素位置(只需判断新增高位是否为1) - **负载因子选择**:0.75在时间与空间成本间取得平衡[^1] #### 二、基本使用方法 ```java // 1. 初始化 HashMap<String, Integer> map = new HashMap<>(); // 2. 添加数据 map.put("apple", 1); map.put("banana", 2); // 3. 获取数据 int count = map.get("apple"); // 返回1 // 4. 遍历操作 for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // 5. 特殊值支持 map.put(null, 0); // 允许null键 map.put("empty", null); // 允许null值[^1] ``` #### 三、典型应用场景 1. **数据缓存**:快速键值查询 2. **频率统计**:如统计单词出现次数 3. **对象映射**:存储配置参数 4. **JSON数据处理**:键值对结构天然对应JSON格式[^3] #### 四、关键特性对比 | 特性 | 说明 | |--------------------|-----------------------------| | 线程安全 | 非线程安全,需用`ConcurrentHashMap` | | 时间复杂度 | 平均O(1),最差O(log n)(红黑树) | | 存储顺序 | 不保证插入顺序 | | Null支持 | 允许null键和null值 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值