HashMap 深入揭秘:从入门到大厂必备知识!

开发 前端
HashMap 就像是一个超级高效的存储工具,它的核心理念是:通过哈希算法定位存储位置,快速存取数据。接下来,我们就一起探讨它的实现原理。

引言

Hi,大家好,我是小米,一个喜欢分享技术的29岁程序员。今天和大家聊聊一个在Java面试中非常经典的问题:“说一下 HashMap 的实现原理?”。别着急,我会用讲故事的方式,把它掰开了揉碎了讲清楚,让你听完之后,再也不怕这个问题!

故事的开头:一个简单的需求

一天,产品经理找到你,说用户需要存储一组“键值对”,并且希望能通过“键”快速找到对应的“值”。作为一个有经验的开发者,你第一时间就想到了Java里的HashMap,对吧?

HashMap 就像是一个超级高效的存储工具,它的核心理念是:通过哈希算法定位存储位置,快速存取数据。接下来,我们就一起探讨它的实现原理。

HashMap 的基本构造

1. 底层数据结构

HashMap 的底层是由一个数组和多个链表/红黑树组成的。这种结构有个专业名词,叫做“拉链法”。我们可以把它想象成一个巨大的仓库,里面有许多货架,每个货架上又挂了很多小篮子:

  • 数组:仓库的货架,负责存储数据的入口;
  • 链表/红黑树:每个篮子,存储可能冲突的键值对。

2. 核心字段

HashMap 有几个非常重要的字段,分别是:

  • 容量(capacity):数组的大小,默认是16;
  • 负载因子(loadFactor):控制数组什么时候扩容,默认是0.75;
  • 阈值(threshold):容量 × 负载因子,当元素个数超过这个值时,就需要扩容。

如何存储数据?

当我们调用 put(K key, V value) 方法时,HashMap 会经历以下几个步骤:

1. 计算哈希值

通过 key 的 hashCode() 方法计算哈希值,并通过一个位运算 h & (n - 1) 确定该数据应该存储到数组的哪个索引上。

为什么不用取模,而用位运算呢?

因为位运算比取模快很多,尤其是在需要高性能的场景下。

2. 定位桶(Bucket)

找到数组的对应位置,查看这个位置上是否已经有数据:

  • 如果没有数据,直接存储;
  • 如果已经有数据,则发生了哈希冲突。

3. 解决哈希冲突

哈希冲突是不可避免的,HashMap 通过两种方式来解决:

  • 链表:在同一个索引处存储多个键值对;
  • 红黑树:当链表的长度超过8时,会转化为红黑树,提升查找效率。

4. 插入数据

将新的键值对插入到对应的链表或红黑树中。如果 key 已经存在,会覆盖旧的 value。

如何取出数据?

当我们调用 get(K key) 方法时,HashMap 会进行以下操作:

1. 再次计算哈希值

通过 key 的 hashCode() 方法计算哈希值,定位到数组的某个索引。

2. 查找桶内数据

  • 进入桶中,依次遍历链表或红黑树:
  • 如果找到对应的 key,就返回 value;
  • 如果遍历完没有找到,返回 null。

注意:这里的 key 比较是通过 equals() 方法完成的。

扩容机制

当 HashMap 的元素数量超过阈值(capacity × loadFactor)时,就会触发扩容:

  • 数组的大小变为原来的两倍;
  • 重新计算每个键值对的哈希值,并放入新的数组中。

扩容是个性能开销较大的操作,所以在使用 HashMap 时,我们通常会预估大小,尽量减少扩容次数。

故事的高潮:面试官的深挖

当你讲完上述内容,面试官可能会进一步问你一些细节问题:

1. 为什么数组大小是2的幂次?

因为 h & (n - 1) 的位运算只有在数组大小是2的幂次时,才能均匀分布哈希值,减少冲突。

2. 红黑树是如何提高效率的?

链表的时间复杂度是 O(n),而红黑树的查找复杂度是 O(log n)。当链表太长时(超过8),会自动转化为红黑树,显著提高查找效率。

3. HashMap 是线程安全的吗?

HashMap 不是线程安全的。如果在多线程环境下使用,可以考虑使用 ConcurrentHashMap。

故事的结尾:小技巧

最后,分享几个关于 HashMap 的小技巧:

  • 合理设置初始容量:避免频繁扩容,提升性能;
  • 重写 hashCode() 和 equals() 方法:确保键的哈希值均匀分布,减少冲突;
  • 多线程场景用 ConcurrentHashMap:避免线程安全问题。

责任编辑:武晓燕 来源: 软件求生
相关推荐

2023-06-27 08:34:32

2010-06-07 18:51:15

UML入门

2023-10-18 18:32:07

2023-07-26 07:15:13

HashMapArrayListLinkedList

2020-05-09 11:26:43

ChromeFirefoxWindows

2024-09-06 17:45:55

Linux磁盘

2017-06-26 09:15:39

SQL数据库基础

2017-07-25 16:35:12

LSTM深度学习自然语言

2016-12-08 15:36:59

HashMap数据结构hash函数

2012-02-29 00:49:06

Linux学习

2020-09-08 18:37:49

TypeScript开发前端

2013-06-06 13:42:48

OSPF入门配置

2016-12-30 17:04:57

2017-01-05 21:05:40

2010-02-06 15:31:18

ibmdwAndroid

2009-07-22 14:55:16

ibmdwAndroid

2016-12-08 22:39:40

Android

2019-07-02 14:17:18

API网关网关流量

2021-02-21 22:53:01

CanvasHTML5JavaScript

2021-09-01 22:58:22

Canvas标签
点赞
收藏

51CTO技术栈公众号