性能优化——HashMap的设计与优化

本文详细探讨了Java中HashMap的数据结构、工作原理以及在性能优化方面的策略。通过分析HashMap的内部实现,包括哈希函数、扩容机制和并发问题,揭示了其高效查找和存储的秘密。同时,文章还讨论了在高并发场景下,如何利用ConcurrentHashMap来进一步提升性能。

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

常用的数据结构
 
我在 05 讲分享 List 集合类的时候,讲过 ArrayList 是基于数组的数据结构实现的, LinkedList 是基于链表的数据结构实现的,而我今天要讲的 HashMap 是基于哈希表的数据结构实现的。我们不妨一起来温习下常用的数据结构,这样也有助于你更好地理解后面地 内容。
 
数组 :采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为 O(1), 但在数组中间以及头部插入数据时,需要复制移动后面的元素。
 
链表 :一种在物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的。
 
链表由一系列结点(链表中每一个元素)组成,结点可以在运行时动态生成。每个结点都包 含“存储数据单元的数据域”和“存储下一个结点地址的指针域”这两个部分。
 
由于链表不用必须按顺序存储,所以链表在插入的时候可以达到 O(1) 的复杂度,但查找一 个结点或者访问特定编号的结点需要 O(n) 的时间。
 
哈希表 :根据关键码值(Key value)直接进行访问的数据结构。通过把关键码值映射到表 中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数,存放记录的数组 就叫做哈希表。
 
:由 n(n≥1)个有限结点组成的一个具有层次关系的集合,就像是一棵倒挂的树。
 
HashMap 的实现结构
 
了解完数据结构后,我们再来看下 HashMap 的实现结构。作为最常用的 Map 类,它是基 于哈希表实现的,继承了 AbstractMap 并且实现了 Map 接口。
 
哈希表将键的 Hash 值映射到内存地址,即根据键获取对应的值,并将其存储到内存地址。 也就是说 HashMap 是根据键的 Hash 值来决定对应值的存储位置。通过这种索引方式, HashMap 获取数据的速度会非常快。
 
例如,存储键值对(x,“aa”)时,哈希表会通过哈希函数 f(x) 得到"aa"的实现存储位
置。
 
但也会有新的问题。如果再来一个 (y,“bb”),哈希函数 f(y) 的哈希值跟之前 f(x) 是一 样的,这样两个对象的存储地址就冲突了,这种现象就被称为哈希冲突。 那么哈希表是怎么 解决的呢?方式有很多,比如,开放定址法、再哈希函数法和链地址法。
 
开放定址法很简单,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空
位置,那么可以把 key 存放到冲突位置的空位置上去。这种方法存在着很多缺点,例如,
查找、扩容等,所以我不建议你作为解决哈希冲突的首选。
 
再哈希法顾名思义就是在同义词产生地址冲突时再计算另一个哈希函数地址,直到冲突不再
发生,这种方法不易产生“聚集”,但却增加了计算时间。如果我们不考虑添加元素的时间
成本,且对查询元素的要求极高,就可以考虑使用这种算法设计。
 
HashMap 则是综合考虑了所有因素,采用链地址法解决哈希冲突问题。这种方法是采用了
数组(哈希表)+ 链表的数据结构,当发生哈希冲突时,就用一个链表结构存储相同 Hash
值的数据。
 
HashMap 的重要属性
 
从 HashMap 的源码中,我们可以发现,HashMap 是由一个 Node 数组构成,每个 Node 包含了一个 key-value 键值对。
 
transient Node<K,V>[] table;
 
Node 类作为 HashMap 中的一个内部类,除了 key、value 两个属性外,还定义了一个 next 指针。当有哈希冲突时,HashMap 会用之前数组当中相同哈希值对应存储的 Node 对象,通过指针指向新增的相同哈希值的 Node 对象的引用。
 
  static class Node<K,V> implements Map.Entry<K,V> {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值