Node.js v26.0 给Map和WeakMap加了两个新方法:getOrInsert 和 getOrInsertComputed。
getOrInsert 接收一个默认值。如果键存在,直接返回对应的值;如果不存在,插入这个默认值,然后返回。
js
// 以前
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(value);
js
// 以后
map.getOrInsert(key, []).push(value);
一行搞定。
你看看这个对比,是不是爽多了?
getOrInsertComputed 接收一个回调函数,只在需要插入的时候才调用。适合默认值计算开销比较大的场景。
js
// 计算默认值是很贵的操作
map.getOrInsertComputed(key, () => cpuIntensiveComputation(key));
如果键已经存在,回调根本不会执行,省时省力。
实战案例:分组、计数
我们来几个真实场景,感受一下。
场景一:分组
以前写分组:
js
let grouped = new Map();
for (let [key, ...values] of data) {
if (grouped.has(key)) {
grouped.get(key).push(...values);
} else {
grouped.set(key, values);
}
}
用新API:
js
let grouped = new Map();
for (let [key, ...values] of data) {
grouped.getOrInsert(key, []).push(...values);
}
代码量直接砍半。
场景二:计数器
以前写计数器:
js
let counts = new Map();
if (counts.has(key)) {
counts.set(key, counts.get(key) + 1);
} else {
counts.set(key, 1);
}
上述代码我们在 LeetCode 考察 HashMap 题目中屡见不鲜。
用新API:
js
let counts = new Map();
counts.set(key, counts.getOrInsert(key, 0) + 1);
getOrInsert(key, 0)保证了初始值0存在,然后直接加1。
性能的考量
有人可能会问,getOrInsert内部是怎么实现的?
提案的设计保证了一次查找搞定。你可以把它想象成get,如果没找到就set,然后返回。整个过程只做一次哈希查找。
那getOrInsertComputed呢?也是只查一次,而且回调只在需要插入时才调用,不会有额外开销。
对比传统的has + get/set,那是两次甚至三次查找。所以新API不仅更简洁,理论上还更快。
那WeakMap呢?
提案同时给WeakMap也加了同样的方法。
用法一模一样,但要注意WeakMap的键必须是对象,默认值回调里返回的也得是对象。
js
let wm = new WeakMap();
let obj = {};
wm.getOrInsert(obj, { count: 0 });
WeakMap的场景主要是缓存临时数据,有了这个方法就不用每次先has了。
最后问大家
下面两段代码有什么问题?
js
map.set(key, map.get(key) || defaultValue);
js
map.set(key, map.get(key) ?? defaultValue);
或者说用 getOrInsert 除了简洁能解决什么问题?