HashMap

本文详细介绍了HashMap在JDK1.8中的实现原理,包括其数据结构从数组+链表到数组+链表+红黑树的演变,以及为何使用2的次幂作为容量。文章还讨论了负载因子、扩容策略(当元素数量达到容量的0.75倍时扩容)和链表树化(链表长度超过8时转换为红黑树)等关键特性。此外,作者强调了初始化容量的重要性,以减少不必要的扩容操作,并提供了阿里规约中的初始化容量计算方式。

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

        HashMap是我们日常开发中几乎每天都要用到的集合类,它是以k-v的方式进行存储的。在jdk1.7到jdk1.8HashMap的实现略有不同,其中两个重要的区别是在1.7中HashMap采用的数据结构是数组+链表,但是到了1.8中HashMap采用数组+链表+红黑树。红黑树的引用是为了提高它的查询效率,因为链表查询的时间复杂度是O(n),而红黑树的时间复杂度是O(log(n))。还有一个就是当我们发生Hash碰撞时1.7采用头插法,而1.8采用为尾插法。当然1.7到1.8还有很多优化的细节,比如对它的hash算法进行简化。

        接下来我就以jdk1.8来和你聊一聊HashMap的基本实现原理。首先在我们创建HashMap时,阿里规约里要求我们传入一个初始化容量,就是说在我们预知将来要插入多少个数据的情况下最好传入一个初始化容量,而且这个容量最好是2的次幂。当然如果不传入初始化容量,HashMap的默认容量是16.

        接下来我会和你聊聊为什么是2的次幂。首先当我们往HashMap里put值的时候,当put第一个值时,HashMap的数组会被初始化。按照我们传入的初始化容量,大于等于这个初始化容量的最近的一个2的次幂进行初始化数组。初始化之后他会用key的hash值 & (容量 - 1)算出他的下标。key的hash算法用了扰动函数

(h = key.hashCode()) ^ (h >>> 16)  这样可以使高16位与低16位运算,增加了随机性可以减少hash冲突。为什么我们之所以采用与运算而不是取模,因为与运算在计算机中的效率非常高,所以HashMap采用与运算的方式

        当然在我们添加数据的时候会发生避免不了的两个问题,一个就是扩容的问题,另一个就是树化的问题。

        关于扩容的问题,在HashMap中有一个成员变量,它叫做负载因子默认是0.75。当我们HashMap的size也就是插入节点的数量大于等于HashMap的容量 * 负载因子时就会进行扩容。扩容的方式是 * 2,这样也保证了容量是2的次幂。

        当我们一条链表上的数据足够多的时候,就可能会进行树化。树化的前提就是链表上的节点大于等于8个,当然只有这一个条件还不够,在HashMap的源码中还有个成员变量,就是最小的树化容量,默认是64。也就是我们数组的容量达不到64的时候他会优先选择扩容而不是对链表进行树化。所以树化有两个先决条件,一个是容量大于等于64,另一个就是一条链表上的节点大于等于8.当节点小于等于6的时候会把树转为链表。

        树化和扩容都比较耗性能。所以在阿里规约中要求我们初始化容量的根本目的就是文为了让我们减少扩容的次数。阿里规约中给了一个初始化容量的计算的方式,我记得应该是要存入数据的数量除以负载因子然后再加一

### JavaHashMap 的使用方法 #### 1. **简介** `HashMap` 是 Java 集合框架中的一个重要类,用于存储键值对(key-value pair)。它的底层基于哈希表实现,提供了快速的查找、插入删除操作。`HashMap` 不保证元素的顺序,并允许一个 `null` 键多个 `null` 值。 #### 2. **基本操作** 以下是 `HashMap` 的一些常用方法及其功能: - **put(key, value)**: 将指定的键值对存入 `HashMap`。 - **get(key)**: 返回与指定键关联的值。 - **remove(key)**: 移除指定键对应的映射关系。 - **size()**: 返回 `HashMap` 中键值对的数量。 - **clone()**: 创建并返回该 `HashMap` 对象的一个副本。 - **isEmpty()**: 如果此 `HashMap` 映射不包含任何键值对,则返回 true。 这些方法的具体用法可以通过下面的例子来说明。 --- #### 3. **示例代码** ##### 示例 1: 添加键值对并获取大小 ```java // Java program to demonstrate the use of size() method in HashMap import java.util.*; public class SizeExample { public static void main(String[] args) { // Create an empty HashMap Map<Integer, String> map = new HashMap<>(); // Add key-value pairs using put() map.put(10, "C"); map.put(20, "C++"); map.put(50, "JAVA"); map.put(40, "PHP"); map.put(30, "SFDC"); // Print the HashMap content System.out.println("HashMap Content: " + map); // Get the number of entries in the HashMap int size = map.size(); System.out.println("Size of HashMap: " + size); } } ``` 这段代码展示了如何向 `HashMap` 插入数据以及计算其大小[^1]。 --- ##### 示例 2: 删除特定键值对 ```java // Java program to demonstrate the removal operation in HashMap import java.util.*; public class RemoveExample { public static void main(String[] args) { // Initialize a HashMap with some data Map<Integer, String> map = new HashMap<>(); map.put(10, "C"); map.put(20, "C++"); map.put(50, "JAVA"); map.put(40, "PHP"); map.put(30, "SFDC"); // Display initial state System.out.println("Initial HashMap: " + map); // Remove entry associated with key '50' map.remove(50); // Show updated HashMap after deletion System.out.println("Updated HashMap after removing key '50': " + map); } } ``` 这里演示了如何移除某个键所对应的数据项[^2]。 --- ##### 示例 3: 复制一份新的 HashMap 实例 ```java // Example demonstrating cloning functionality within HashMaps. import java.util.*; public class CloneExample { public static void main(String[] args) { // Original HashMap creation and population HashMap<Integer, String> originalMap = new HashMap<>(); originalMap.put(10, "C"); originalMap.put(20, "C++"); originalMap.put(50, "JAVA"); originalMap.put(40, "PHP"); originalMap.put(30, "SFDC"); // Cloning process begins here HashMap<Integer, String> clonedMap = (HashMap<Integer, String>)originalMap.clone(); // Output both maps post-cloning action System.out.println("Original HashMap Contents: " + originalMap); System.out.println("Cloned HashMap Contents: " + clonedMap); } } ``` 本部分解释了复制现有 `HashMap` 的过程[^3]。 --- #### 4. **性能特点与其他注意事项** 由于 `HashSet` 内部依赖于 `HashMap` 来管理其成员集合,因此它们共享相似的时间复杂度特性——平均情况下 O(1),最坏情况取决于冲突处理机制[^4]。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w7486

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值