首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

谈谈我对HashMap扩容机制的理解及底层实现

  • 25-02-22 09:01
  • 3633
  • 10359
blog.csdn.net

目录

一、HashMap的底层实现

二、HashMap扩容机制

概念

详细扩容:

1、初始容量

2、添加元素

3、元素数量检查

4、触发扩容

5、迁移元素

6、更新容量和阈值

代码:


一、HashMap的底层实现

HashMap 是 Java 中常用的数据结构之一,用于存储键值对。它的底层实现是基于哈希表(Hash Table)。以下是 HashMap 的底层实现细节:

  1. 数组: HashMap 内部维护一个数组,数组的每个元素称为桶(bucket)。数组的长度通常是2的幂,这是为了便于哈希函数计算索引值。

  2. 链表和红黑树: 在每个桶中,如果发生哈希冲突(即两个不同的键具有相同的哈希值),那么这些键值对会以链表的形式存储在同一个桶中。从Java 8 开始,当链表的长度超过一定阈值时,会将链表转换为红黑树,以提高查询性能。

  3. 哈希函数: HashMap 使用键的哈希码来计算索引值。哈希码是通过调用键的 hashCode() 方法得到的。计算索引值的过程涉及到取模运算,即 index = hashCode % arrayLength。

  4. 扩容: 当 HashMap 中的元素个数超过了容量与负载因子的乘积时,就会触发扩容操作。负载因子是一个表示填充程度的浮点数,默认为0.75。扩容时,数组的长度会变为原来的两倍,并且原来的键值对需要重新计算哈希码和索引值,然后放入新的数组中。

  5. 并发性: HashMap 在多线程环境下是不安全的,因为多个线程可能同时修改 HashMap,导致数据不一致。在Java 8及之后的版本中,提供了 ConcurrentHashMap 来解决这个问题,它通过分段锁和 CAS 操作来保证并发安全性。

参考资料

HashMap 源码 、TreeBin 类(红黑树容器)

二、HashMap扩容机制

概念

HashMap 的扩容是为了保持其在负载因子(load factor)范围内的性能。负载因子是一个表示填充程度的浮点数,默认值为 0.75。当 HashMap 中的元素数量达到容量与负载因子的乘积时,就会触发扩容操作。

详细扩容:

1、初始容量

当创建一个新的 HashMap 时,它会有一个初始容量,通常是16。这个初始容量可以在构造函数中指定,但如果不指定,默认值为16。

2、添加元素

当往 HashMap 中添加键值对时,首先计算键的哈希码,并根据哈希码计算索引值。如果该索引位置没有元素,直接插入;如果有元素,发生哈希冲突,就会以链表的形式添加到相应的桶中。

3、元素数量检查

在每次添加元素后,HashMap 会检查当前元素的数量是否超过了容量与负载因子的乘积,即 size > threshold = capacity * loadFactor。

4、触发扩容

如果元素数量超过了阈值,就会触发扩容操作。扩容时,HashMap 将会创建一个新的数组,长度是原数组的两倍(newCapacity = oldCapacity * 2),然后将原数组中的元素重新计算哈希码和索引值,放入新数组中。

5、迁移元素

扩容后,原数组中的每个桶可能包含一个链表或一棵红黑树。HashMap 将遍历原数组中的每个桶,然后将其中的元素迁移到新数组中。在迁移的过程中,由于新数组长度是原数组长度的两倍,因此每个元素的索引值可能会发生变化。

6、更新容量和阈值

扩容后,HashMap 更新自己的容量和阈值。容量变为新数组的长度,阈值变为新容量与负载因子的乘积。

代码:

  1. import java.util.Arrays;
  2. import java.util.LinkedList;
  3. public class MyHashMap<K, V> {
  4. private static final int DEFAULT_INITIAL_CAPACITY = 16;
  5. private static final float DEFAULT_LOAD_FACTOR = 0.75f;
  6. private Node<K, V>[] table;
  7. private int size;
  8. private int threshold;
  9. @SuppressWarnings("unchecked")
  10. public MyHashMap() {
  11. table = new Node[DEFAULT_INITIAL_CAPACITY];
  12. threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
  13. }
  14. public void put(K key, V value) {
  15. if (key == null) {
  16. throw new IllegalArgumentException("Key cannot be null");
  17. }
  18. if (size + 1 > threshold) {
  19. resize();
  20. }
  21. int hash = hash(key);
  22. int index = getIndex(hash, table.length);
  23. if (table[index] == null) {
  24. table[index] = new Node<>(hash, key, value);
  25. size++;
  26. } else {
  27. LinkedList<Node<K, V>> bucket = table[index].getBucket();
  28. for (Node<K, V> node : bucket) {
  29. if (node.key.equals(key)) {
  30. // Key already exists, update the value
  31. node.value = value;
  32. return;
  33. }
  34. }
  35. // Key does not exist in the bucket, add a new node
  36. bucket.add(new Node<>(hash, key, value));
  37. size++;
  38. }
  39. }
  40. private void resize() {
  41. int oldCapacity = table.length;
  42. int newCapacity = oldCapacity * 2;
  43. threshold = (int) (newCapacity * DEFAULT_LOAD_FACTOR);
  44. Node<K, V>[] newTable = new Node[newCapacity];
  45. for (Node<K, V> node : table) {
  46. if (node != null) {
  47. LinkedList<Node<K, V>> bucket = node.getBucket();
  48. for (Node<K, V> entry : bucket) {
  49. int hash = hash(entry.key);
  50. int index = getIndex(hash, newCapacity);
  51. if (newTable[index] == null) {
  52. newTable[index] = new Node<>(hash, entry.key, entry.value);
  53. } else {
  54. newTable[index].getBucket().add(new Node<>(hash, entry.key, entry.value));
  55. }
  56. }
  57. }
  58. }
  59. table = newTable;
  60. }
  61. private int hash(K key) {
  62. // Simplified hash function for illustration purposes
  63. return key.hashCode();
  64. }
  65. private int getIndex(int hash, int length) {
  66. // Simplified index calculation for illustration purposes
  67. return hash % length;
  68. }
  69. private static class Node<K, V> {
  70. private final int hash;
  71. private final K key;
  72. private V value;
  73. private LinkedList<Node<K, V>> bucket;
  74. public Node(int hash, K key, V value) {
  75. this.hash = hash;
  76. this.key = key;
  77. this.value = value;
  78. this.bucket = new LinkedList<>();
  79. this.bucket.add(this);
  80. }
  81. public LinkedList<Node<K, V>> getBucket() {
  82. return bucket;
  83. }
  84. }
  85. public static void main(String[] args) {
  86. MyHashMap<String, Integer> myHashMap = new MyHashMap<>();
  87. myHashMap.put("One", 1);
  88. myHashMap.put("Two", 2);
  89. myHashMap.put("Three", 3);
  90. // ... (additional testing and usage)
  91. }
  92. }

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览56407 人正在系统学习中
注:本文转载自blog.csdn.net的不想步入秃头的年龄的文章"https://blog.csdn.net/AliceNo/article/details/134825109"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top