在 JavaScript 中,Object
和 Map
都可以用于“字典”(键值对存储),因为它们允许您执行所有基本的读写操作当使用这样的数据结构时,您会期望:“设置”、“获取”、“删除”和“检测”键、值和条目。从历史上看,在 JavaScript 中存在 Map
之前,我们就一直使用 Object
来存储字典。这完全没问题,但是,在某些情况下,有一些重要的差异使得 Map
更可取,可以在此处找到: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#objects_vs._maps
就我个人而言,就我目前正在处理的情况而言,我希望使用 Map
而不是 Object
,因为我觉得 Map
会更适合我目前想要实现的目标:包含数亿条目的庞大字典、大量读/写操作,并且基于自定义基准,Map
似乎比 Object 快得多
(同样,就我而言)。
不幸的是,当迭代我的 Map
字典时(Map
是可迭代的,因此可以直接使用 for...of
进行迭代),我面临着“迭代和变异”问题:在Map
上进行迭代时,您不应该在迭代中改变相同的Map
。这是一种反模式!
对于Object
(它不实现迭代协议(protocol),因此默认情况下不能使用for...of
直接迭代对象),我使用Object.keys()
、Object.values()
或 Object.entries()
返回给定字典自身可枚举属性名称的数组(keys )、值或字符串键控属性 [key, value] 对。因此,因为我不是在字典本身上进行迭代,而是实际上在内存中的一个单独的数组上进行迭代,所以我可以安全地写入/改变我的字典,而不会遇到与“迭代和改变”相关的任何问题。 “问题。
当使用 Map
作为字典时,为了迭代和变异,一种解决方案是克隆(浅复制或深复制)字典,然后迭代原始字典,但仅变异克隆。当迭代周期结束时,将克隆分配给原始字典并删除现在无用的克隆。这需要在内存中克隆原始 Map
字典,这会占用不必要的空间。
是否有比克隆更好的方法来规避与 Map
字典相关的“Iterate-and-Mutate”反模式/问题?
谢谢!
示例:
对象字典 -> 有效!
// Object dictionary -> WORKS!
const numbers = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
};
function compute(numbers) {
/*
* The Object.values() method returns an array of a given object's own
* enumerable property values, in the same order as that provided by a
* for...in loop.
*/
for (number of Object.values(numbers)) {
console.log('number:', number);
const square = number * number;
if (!numbers[`${square}`]) {
numbers[`${square}`] = square;
}
}
return numbers;
}
console.time('timer');
const result = compute(numbers);
console.timeEnd('timer');
console.log('result:', result);
map 字典 -> 不能按原样工作!
// Map dictionary -> DOESN'T WORK AS IS!
const numbers = new Map([
['0', 0],
['1', 1],
['2', 2],
['3', 3],
['4', 4],
['5', 5],
]);
function compute(numbers) {
/*
* The values() method returns a new iterator object that contains the values
* for each element in the Map object in insertion order.
*/
for (number of numbers.values()) {
console.log('number:', number);
const squared = number * number;
if (!numbers.has(`${squared}`)) {
// "Iterate-and-Mutate" anti-pattern/problem:
numbers.set(`${squared}`, squared);
}
}
return numbers;
}
console.time('timer');
const result = compute(numbers);
console.timeEnd('timer');
console.log('result:', result);
映射字典 + 克隆(深度复制)-> 有效!
(速度慢且占用大量内存)
// Map dictionary + clone (Deep Copy) -> WORKS!
let numbers = new Map([
['0', 0],
['1', 1],
['2', 2],
['3', 3],
['4', 4],
['5', 5],
]);
function compute(numbers) {
/*
* We create a clone (Deep Copy).
* The data itself is cloned (slow and take a lot of memory).
*/
const clone = new Map([ ...numbers.entries() ]);
// Then, we iterate on the original dictionary.
for (number of numbers.values()) {
console.log('number:', number);
const square = number * number;
if (!clone.has(`${square}`)) {
// But we mutate the clone, not the original dictionary.
clone.set(`${square}`, square);
}
}
// Finally, we assign 'clone' to original dictionary 'numbers'.
numbers = clone;
// And delete 'clone'.
delete clone;
return numbers;
}
console.time('timer');
const result = compute(numbers);
console.timeEnd('timer');
console.log('result:', result);
映射字典 + 克隆(浅复制)-> 有效!
(比深复制更快,占用的内存更少,但仍然......)
// Map dictionary + clone (Shallow Copy) -> WORKS!
let numbers = new Map([
['0', 0],
['1', 1],
['2', 2],
['3', 3],
['4', 4],
['5', 5],
]);
function compute(numbers) {
/*
* We create a clone (Shallow Copy).
* The data itself is not cloned (faster and take less memory than Deep Copy).
*/
const clone = new Map(numbers);
// Then, we iterate on the original dictionary.
for (number of numbers.values()) {
console.log('number:', number);
const square = number * number;
if (!clone.has(`${square}`)) {
// But we mutate the clone, not the original dictionary.
clone.set(`${square}`, square);
}
}
// Finally, we assign 'clone' to original dictionary 'numbers'.
numbers = clone;
// And delete 'clone'.
delete clone;
return numbers;
}
console.time('timer');
const result = compute(numbers);
console.timeEnd('timer');
console.log('result:', result);
map 字典 + 假克隆 -> 不起作用,因为不够!
// Map dictionary + fake clone -> DOESN'T WORK BECAUSE INSUFFICIENT!
let numbers = new Map([
['0', 0],
['1', 1],
['2', 2],
['3', 3],
['4', 4],
['5', 5],
]);
function compute(numbers) {
/*
* We create a "clone" (we believe we do, but in reality we don't!).
* The 'clone' variable is not really a clone/copy of 'numbers'.
* It's just a variable that points to the same data in memory.
* Therefore, any mutation on 'numbers' will be reflected on 'clone',
* which breaks the solution!
*/
const clone = numbers;
// Then, we iterate on the original dictionary.
for (number of numbers.values()) {
console.log('number:', number);
const square = number * number;
if (!clone.has(`${square}`)) {
// But we mutate the clone, not the original dictionary.
clone.set(`${square}`, square);
}
}
// Finally, we assign 'clone' to original dictionary 'numbers'.
numbers = clone;
// And delete 'clone'.
delete clone;
return numbers;
}
console.time('timer');
const result = compute(numbers);
console.timeEnd('timer');
console.log('result:', result);
最佳答案
如果您想模仿普通对象使用的模式(即迭代 Object.values
),则迭代 [...numbers.values()]
,这实际上是 Map 的等价物。空间成本相似——在这两种情况下都会创建一个包含值的数组。
所以:
const numbers = new Map([['0', 0],['1', 1],['2', 2],['3', 3],['4', 4],['5', 5]]);
function compute(numbers) {
for (const number of [...numbers.values()]) {
console.log('number:', number);
const squared = number * number;
if (!numbers.has(`${squared}`)) {
numbers.set(`${squared}`, squared);
}
}
return numbers;
}
console.log('result:', ...compute(numbers));
关于javascript - 字典:JavaScript 中的映射与对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71820682/