weakmap 适合存储父子映射,因其键为弱引用对象,可避免内存泄漏;需配合 map 或数组实现双向查询,但不支持遍历、多父场景或图算法。

WeakMap 本身不直接处理图结构的父子关系映射,它只提供一种“弱引用”的键值存储机制——键必须是对象,且不会阻止键对象被垃圾回收。在图结构中,若用 WeakMap 存储父子关系(比如 parentMap.set(childNode, parentNode)),它能避免内存泄漏,但不能自动维护或推导关系,需配合手动逻辑使用。
为什么 WeakMap 适合存父子映射?
图节点通常是对象(如自定义 Node 类实例或 DOM 元素)。WeakMap 的键是弱引用,当某个节点对象不再被其他地方引用时,即使它还在 WeakMap 中作为键,也会被自动回收——这防止了因映射表长期持有节点引用而导致的内存泄漏。
- DOM 节点、虚拟树节点、临时计算图节点等生命周期不确定的场景尤其适用
- 不需要手动清理映射表,GC 会一并回收键和对应值
- 无法用非对象类型(如字符串 ID)作键,所以天然绑定到节点实例本身
如何用 WeakMap 表达父子关系?
常见做法是用两个 WeakMap 分别记录单向关系:一个存子→父,另一个可选存父→子列表(注意后者需用普通 Map 或数组,因为 WeakMap 不支持遍历,也无法用节点作值)。
-
子→父映射:用
WeakMap<node node null></node>,每个子节点映射到其唯一父节点(适合树或有向无环图) -
父→子映射:不能直接用 WeakMap(因为子节点是值,不是键),可用
Map<node node></node>或为每个父节点维护一个子节点数组 - 若需双向查询,建议组合使用:
parentMap(WeakMap) +childrenMap(Map)
实际写法示例
假设有一个简单节点类:
class Node {
constructor(value) {
this.value = value;
}
}
const parentMap = new WeakMap(); // child → parent
const childrenMap = new Map(); // parent → child[]
function setParent(child, parent) {
parentMap.set(child, parent);
if (!childrenMap.has(parent)) {
childrenMap.set(parent, []);
}
childrenMap.get(parent).push(child);
}
function getParent(node) {
return parentMap.get(node) ?? null;
}
function getChildren(node) {
return childrenMap.get(node) ?? [];
}
删除节点时,只需解除外部引用,WeakMap 中的条目会随 child 自动消失;childrenMap 需要显式清理(或依赖应用层管理)。
注意事项与局限
WeakMap 不是图算法引擎,它只是底层存储工具:
- 不支持遍历键或值,无法枚举所有父子对,调试或分析时需额外日志或快照
- 无法处理多父场景(如 DAG 中节点有多个前驱)——WeakMap 值只能存一个,需改用
WeakMap<node set>></node>存前驱集合 - 不能替代图遍历逻辑(如 DFS/BFS),仍需手动实现路径查找、环检测等
- 序列化时 WeakMap 内容不可导出,持久化需另行设计
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











