WeakHashMap 是 Java 中一个使用弱引用存储键的 Map 实现。它允许键是弱引用,这意味着当没有其他强引用引用这些键时,键可以被垃圾回收器回收。这使得 WeakHashMap 适合用于缓存场景,其中键的生命周期不需要超过对键的最后一次引用。由于键是弱引用,WeakHashMap 不能保证键值对的永久存储,因此在访问时可能会发现一些键值对已经被回收。WeakHashMap 是非同步的,适用于单线程环境或需要快速访问的场景。
1、 WeakHashMap
WeakHashMap 是 Java 中的一种哈希表实现,它使用弱引用来存储键。这意味着当键不再被其他强引用引用时,它们可以被垃圾回收器回收,即使WeakHashMap中还包含这些键的引用。这种特性使得WeakHashMap常用于缓存实现,其中键的生命周期不需要超过对键的最后一次引用。
设计思考:
- 需求场景:
- 在某些应用中,需要一种映射结构,其中的键是弱引用的,这意味着这些键不会阻止它们所引用的对象被垃圾回收器回收。这在缓存实现或临时映射中非常有用,特别是当希望映射表在不再被外部引用时能够自动释放资源时。
- 现有技术局限性:
- 标准的
HashMap和TreeMap使用强引用存储键,这意味着只要键存在于映射中,即使没有其他引用,对象也不会被垃圾回收,这可能导致内存泄漏。
- 标准的
- 技术融合:
- WeakHashMap 结合了哈希表的快速查找特性和弱引用机制,允许键被垃圾回收器自动回收,而不需要显式的删除操作。
- 设计理念:
- WeakHashMap 旨在提供一个映射,其中的键是弱引用的,这意味着如果一个键不再被其他强引用引用,那么它将被垃圾回收器回收,即使它仍然存在于 WeakHashMap 中。
- 实现方式:
- WeakHashMap 内部使用一个数组来存储桶(bucket),每个桶可以包含一个或多个通过链表链接的节点(Entry)。键是弱引用,而值是强引用。当垃圾回收器运行时,它会清除所有没有被强引用的键。
2、 数据结构
图说明:
- WeakHashMap:表示
WeakHashMap类的实例,它使用弱引用来存储键。 - Hash Array:
WeakHashMap使用一个哈希数组来存储桶(Buckets)。 - Bucket 1 & Bucket 2:哈希数组中的每个桶可以包含一个或多个节点(Node),这些节点存储实际的键值对。
- Node:每个桶包含多个节点,这些节点存储键值对,并且通过链表连接。
- Weak Reference to Key:节点中存储的键是弱引用,这意味着如果这些键没有被其他强引用引用,它们可以被垃圾回收器回收。
- Value:节点中存储的值。
- Next Node:节点中的引用,指向链表中的下一个节点。
- Null:链表的末尾节点的 Next Node 指向 Null。
3、 执行流程
图说明:
- 创建 WeakHashMap 实例:初始化
WeakHashMap对象。 - 插入元素(put) :执行将键值对插入到
WeakHashMap的操作。 - 查找元素(get) :执行根据键查找值的操作。
- 删除元素(remove) :执行根据键删除键值对的操作。
- 计算键的哈希码:计算操作键的哈希码以确定其在哈希表中的位置。
- 确定桶索引:根据哈希码确定键在哈希表中的桶索引。
- 检查键是否被回收:检查键是否已被垃圾回收器回收。
- 插入或更新节点:如果桶中没有找到相同的键(基于引用相等性),则在桶的链表中插入新的节点;如果找到,则更新节点的值。
- 遍历桶中的链表:在确定的桶中遍历链表以查找键值对。
- 返回节点值:返回找到节点的值。
- 删除节点:从桶的链表中删除指定的节点。
- 清理被回收的键值对:清理所有被垃圾回收器回收的键值对。
4、优点
- 自动内存管理:
- 键作为弱引用,当没有强引用引用键时,键可以被垃圾回收器回收,从而自动释放内存。
- 减少内存泄漏:
- 有助于减少因键的强引用而导致的内存泄漏问题。
- 灵活的缓存实现:
- 适合实现缓存,其中条目不需要永久存储,且当内存空间不足时可以自动清除。
5、缺点
- 线程不安全:
- WeakHashMap 本身不是线程安全的,需要外部同步来保证线程安全。
- 无法保证键的生命周期:
- 由于键是弱引用,无法保证它们在特定时间内一定存在。
- 可能的不一致性:
- 在迭代映射时,由于键可能在迭代过程中被回收,可能会导致不一致的结果。
6、使用场景
- 缓存实现:
- 适用于实现缓存,其中条目不需要永久存储,且当内存空间不足时可以自动清除。
- 临时映射:
- 当需要临时存储对象映射,且希望映射表在不再被外部引用时能够自动释放资源时。
7、类设计
8、应用案例
WeakHashMap 通常用于实现缓存策略,尤其是在需要自动清理不再使用的对象时。这是一个简单的缓存系统,用于存储用户的会话信息:
import java.util.WeakHashMap;
import java.util.Map;
// 用户会话类
class UserSession {
private String sessionId;
private String userInfo;
public UserSession(String sessionId, String userInfo) {
this.sessionId = sessionId;
this.userInfo = userInfo;
}
// 省略 getter 和 setter 方法
}
// 用户会话管理器
class SessionManager {
private Map<String, UserSession> sessionCache;
public SessionManager() {
// 使用 WeakHashMap 作为缓存,以便在用户会话不再被引用时自动清理
sessionCache = new WeakHashMap<>();
}
// 添加或更新用户会话
public void addOrUpdateSession(String sessionId, UserSession session) {
sessionCache.put(sessionId, session);
}
// 获取用户会话
public UserSession getSession(String sessionId) {
return sessionCache.get(sessionId);
}
// 移除用户会话
public void removeSession(String sessionId) {
sessionCache.remove(sessionId);
}
}
public class Main {
public static void main(String[] args) {
SessionManager sessionManager = new SessionManager();
// 模拟添加用户会话
sessionManager.addOrUpdateSession("session1", new UserSession("session1", "User1 Info"));
sessionManager.addOrUpdateSession("session2", new UserSession("session2", "User2 Info"));
// 获取并打印用户会话信息
UserSession session1 = sessionManager.getSession("session1");
System.out.println("Session 1: " + session1);
UserSession session2 = sessionManager.getSession("session2");
System.out.println("Session 2: " + session2);
// 移除用户会话
sessionManager.removeSession("session1");
}
}