HashMap性能卓越的原因

本文介绍了哈希映射的基本概念,包括散列值的作用及如何处理散列冲突。阐述了如何通过哈希算法将键映射到数组索引,并利用链表解决碰撞问题。最后,解释了查找操作的具体流程。

1、hashcode概念

所有的对象都有一个对应的hachcode(散列值(对应的一个数字)):

如:“rengar”的散列值是1110(假设,实际不是)。

但有的对象散列值会相同,如:“hnu”的散列值为1111,“Dania”散列值也为1111。这个时候就涉及到保存数据的方法了。



2、保存数据

首先设定一个hashcode算法,给每一个字符串配上相应的hashcode,然后设定一个容量为****的数组(****表示不能越界就行),最后将key和value形成一个对应的键值对,存在hashcode作为index的位置。

如:“rengar”和它的value组成键值对存放在数组1110的位置。

但是,因为相同hashcode的原因,导致一些存放的位置上有值,那么就会创建一个链表接在值后面存放。


图片来自:http://how2j.cn/k/collection/collection-hashcode/371.html#nowhere



3、查找数据

如:查找“rengar”,首先要计算它的hashcode值,然后通过这个值作为index去数组中查找到“rengar”。

如果在这个位置不止一个值,那么它会对key进行比较,然后找出正确的value。



总结:HashMap就是用空间换来了时间。

### HashMap线程不安全的原因及表现形式 HashMap 线程不安全的主要原因在于其设计并未考虑多线程环境下的同步问题。具体来说,当多个线程同时对 HashMap 进行读写操作时,可能会出现以下几种问题[^3]。 #### 1. **并发修改导致数据不一致** 在多线程环境下,如果一个线程正在对 HashMap 进行修改(如插入或删除元素),而另一个线程同时尝试访问或修改相同的数据结构,则可能导致数据不一致。例如,线程 A 正在调整 HashMap 的容量(即扩容操作),而线程 B 同时尝试插入新元素,这会导致 HashMap 内部状态混乱。 #### 2. **扩容操作引发的死循环** HashMap 在扩容时会重新计算每个键值对的哈希值,并将它们迁移到新的桶中。此过程涉及复杂的链表重组操作。在 JDK 7 中,由于使用了头插法进行链表迁移,如果两个线程同时执行扩容操作,可能会导致链表形成环形结构。这种情况下,当某个线程尝试遍历该链表时,就会陷入无限循环,最终导致程序崩溃或性能急剧下降[^4]。 #### 3. **丢失更新** 当多个线程同时对同一个键进行更新操作时,可能会发生“丢失更新”的现象。例如,线程 A 和线程 B 都试图更新同一个键的值。假设线程 A 先获取了当前值并进行了修改,但在尚未完成写回操作之前,线程 B 也获取了旧值并完成了更新。结果是线程 A 的更新被线程 B 的更新覆盖,从而导致数据丢失。 #### 4. **脏读** 在某些情况下,一个线程可能读取到另一个线程尚未完全写入的数据。例如,线程 A 正在向 HashMap 中插入一个新键值对,但尚未完成所有必要的初始化操作(如设置节点指针)。此时,线程 B 尝试访问该键值对,可能会读取到部分初始化或错误的数据。 ### 表现形式 HashMap 线程不安全的表现形式主要包括以下几种: - **死循环**:如前所述,JDK 7 中的 HashMap 在扩容时可能会形成循环链表,导致遍历操作进入无限循环。 - **数据丢失**:多个线程同时对同一个键进行更新操作时,可能会导致某些更新被覆盖。 - **数据不一致**:由于缺乏同步机制,不同线程可能看到不同的 HashMap 状态,从而引发逻辑错误。 - **空指针异常**:在极端情况下,线程可能读取到未正确初始化的节点,进而抛出 `NullPointerException`。 ```java // 示例代码:展示 HashMap 线程不安全的问题 import java.util.HashMap; import java.util.Map; public class HashMapThreadUnsafeExample { public static void main(String[] args) throws InterruptedException { Map<Integer, Integer> map = new HashMap<>(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { map.put(i, i); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { map.put(i, i * 2); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final size: " + map.size()); // 输出结果可能小于预期 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值