HashMap工作原理

目录

1.初始化做了哪些事儿?

2.第一次开辟的空间有多少?那初始化时指定开辟空间长度是否更有利于内存资源节省?

3.扩容是在什么时候扩的,达到的阈值是多少?初始申请的空间与扩容阈值之间的关系,围绕第二次扩容阈值与初始指定申请空间的关系说明?

4.模拟写一个新增或删除或扩容的方法

5.是否线程安全?为何不安全?如果不安全如何规避或替代类?

6.在HashMap的使用中,for循环数据过程中删除数据是否异常?如果异常,寻找为何异常?如何规避这个异常?


1.初始化做了哪些事儿?

HashMap的初始化主要包括以下几个方面:

  1. 创建底层为数组的HashMap对象,并将每个桶初始化为空链表。

  2. 根据键的哈希值计算出它在数组中的位置,并将键值对存储到对应的桶中。

  3. 处理哈希冲突,将相同哈希值的键值对存储到同一个桶中形成链表。

  4. HashMap的底层是一个数组,数组中的每个元素被称为桶(bucket),每个桶存储一个链表,链表中的每个节点都是一个键值对。

2.第一次开辟的空间有多少?那初始化时指定开辟空间长度是否更有利于内存资源节省?

当使用默认构造函数创建HashMap对象时,会默认创建一个大小为16的数组。如果能够准确估计HashMap需要存储的键值对数量,指定容量大小进行初始化可以更有效利用内存资源,避免扩容操作。否则,使用默认大小进行初始化也是不错的选择,因为HashMap会自动进行扩容操作。

3.扩容是在什么时候扩的,达到的阈值是多少?初始申请的空间与扩容阈值之间的关系,围绕第二次扩容阈值与初始指定申请空间的关系说明?

当HashMap中存储的键值对数量达到容量的0.75时,就会进行扩容操作。初始申请的空间与扩容阈值有关系,如果我们能够准确估计HashMap需要存储的键值对数量,指定容量大小进行初始化可以更有效利用内存资源,避免过多的扩容操作。第二次扩容阈值为初始容量的0.5。

4.模拟写一个新增或删除或扩容的方法

// 定义哈希函数,使用Python内置的hash()函数
def hash_function(self, key):
    return hash(key)

// 定义扩容方法
def resize(self):
    # 保存旧的存储桶数组
    old_buckets = self.buckets
    # 新容量为旧容量的两倍
    new_capacity = len(old_buckets) * 2
    # 新建存储桶数组
    self.buckets = [[] for _ in range(new_capacity)]
    # 重置元素个数和阈值
    self.size = 0
    self.threshold = int(new_capacity * self.load_factor)

    # 将旧存储桶数组中的元素重新哈希到新的存储桶数组中
    for bucket in old_buckets:
        for key, value in bucket:
            self.add(key, value)

# 定义新增元素方法
def add(self, key, value):
    # 计算哈希值
    hashed_key = self.hash_function(key)
    # 计算存储桶数组下标
    index = hashed_key % len(self.buckets)
    # 获取存储桶
    bucket = self.buckets[index]

    # 遍历存储桶,如果key已存在,则更新value
    for i in range(len(bucket)):
        if bucket[i][0] == key:
            bucket[i] = (key, value)
            return

    # 如果key不存在,则添加新的键值对
    bucket.append((key, value))
    # 元素个数加1
    self.size += 1

    # 如果元素个数超过阈值,则触发扩容
    if self.size >= self.threshold:
        self.resize()

# 定义删除元素方法
def remove(self, key):
    # 计算哈希值
    hashed_key = self.hash_function(key)
    # 计算存储桶数组下标
    index = hashed_key % len(self.buckets)
    # 获取存储桶
    bucket = self.buckets[index]

    # 遍历存储桶,如果找到key,则删除对应的键值对
    for i in range(len(bucket)):
        if bucket[i][0] == key:
            del bucket[i]
            # 元素个数减1
            self.size -= 1
            return

# 定义获取元素方法
def get(self, key):
    # 计算哈希值
    hashed_key = self.hash_function(key)
    # 计算存储桶数组下标
    index = hashed_key % len(self.buckets)
    # 获取存储桶
    bucket = self.buckets[index]

    # 遍历存储桶,如果找到key,则返回对应的value
    for i in range(len(bucket)):
        if bucket[i][0] == key:
            return bucket[i][1]

    # 如果找不到key,则返回None
    return None

5.是否线程安全?为何不安全?如果不安全如何规避或替代类?

该HashMap实现不是线程安全的。因为在多线程环境下,多个线程可能会同时访问和修改存储桶数组,导致数据不一致或者出现竞态条件等问题。

为了规避线程安全问题,可以使用线程安全的数据结构,例如Python标准库中的concurrent.futures.ThreadPoolExecutorconcurrent.futures.ProcessPoolExecutor。也可以使用第三方线程安全的HashMap库,例如concurrent.futures.ThreadPoolExecutorconcurrent.futures.ProcessPoolExecutor

另外,如果需要实现自己的线程安全HashMap,可以使用锁机制来保护存储桶数组的访问和修改。例如,可以为每个存储桶数组的元素维护一个读写锁,保证同一时刻只有一个线程能够对该元素进行读写操作。这种方式虽然可以保证线程安全,但是会增加锁的开销,降低HashMap的性能表现。

6.在HashMap的使用中,for循环数据过程中删除数据是否异常?如果异常,寻找为何异常?如何规避这个异常?

在HashMap的使用中,如果在for循环遍历数据过程中删除数据,可能会抛出`ConcurrentModificationException`异常。这是因为在遍历过程中,如果对HashMap进行修改操作,会导致modCount计数器的值发生变化,而在遍历过程中会检查modCount计数器的值是否发生变化,如果发生变化就会抛出`ConcurrentModificationException`异常。

要规避这个异常,可以使用`Iterator`来遍历HashMap,而不是使用普通的for循环。`Iterator`提供了`remove()`方法来删除元素,并且在删除元素之后会更新modCount计数器的值。示例代码如下:

Map<String, String> map = new HashMap<>();
Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry<String, String> entry = iter.next();
    if (需要删除该元素) {
        iter.remove(); // 使用Iterator的remove方法删除元素
    }
}

另外,如果需要在多线程环境下遍历和修改HashMap,可以使用线程安全的ConcurrentHashMap来代替HashMap,它提供了线程安全的遍历和修改方法,不会抛出`ConcurrentModificationException`异常。

from concurrent.futures import ThreadPoolExecutor
from typing import Dict

# 创建一个ConcurrentHashMap
concurrent_map: Dict[str, str] = {"key1": "value1", "key2": "value2", "key3": "value3"}

# 使用ThreadPoolExecutor提交多个线程任务
with ThreadPoolExecutor(max_workers=10) as executor:
    for key in concurrent_map.keys():
        executor.submit(delete_key, key)

# 打印ConcurrentHashMap的内容
print(concurrent_map)

# 定义一个删除ConcurrentHashMap中指定key的函数
def delete_key(key: str) -> None:
    if key in concurrent_map:
        del concurrent_map[key]

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值