Performance advantages of collection types
ECMAScript 6 introduced two new collection types, Set
and Map
, which not only provide a more intuitive way to manipulate data structures but also significantly outperform traditional object and array implementations in terms of performance. These advantages become even more pronounced when handling large amounts of data.
Performance Advantages of the Set
Type
Set
is a collection that stores unique values. Its internal implementation is based on a hash table, making lookup, insertion, and deletion operations nearly O(1) in time complexity. In contrast, using array methods like includes
or indexOf
for lookups results in a time complexity of O(n).
Lookup Performance Comparison
const array = [1, 2, 3, 4, 5];
const set = new Set(array);
// Array lookup
console.time('Array includes');
console.log(array.includes(3)); // true
console.timeEnd('Array includes'); // ~0.1ms
// Set lookup
console.time('Set has');
console.log(set.has(3)); // true
console.timeEnd('Set has'); // ~0.01ms
When the data size increases to 10,000 elements, the performance advantage of Set
becomes even more evident:
const largeArray = Array.from({ length: 10000 }, (_, i) => i);
const largeSet = new Set(largeArray);
// Array lookup for the last element
console.time('Large Array includes');
console.log(largeArray.includes(9999));
console.timeEnd('Large Array includes'); // ~0.5ms
// Set lookup for the same element
console.time('Large Set has');
console.log(largeSet.has(9999));
console.timeEnd('Large Set has'); // ~0.01ms
Deduplication Performance
Set
inherently supports deduplication, whereas achieving the same with arrays requires additional logic:
const duplicateArray = [1, 2, 2, 3, 4, 4, 5];
// Array deduplication
const uniqueArray = [...new Set(duplicateArray)]; // [1, 2, 3, 4, 5]
This approach is more efficient than traditional array deduplication methods, such as using filter
and indexOf
:
// Traditional array deduplication
const traditionalUnique = duplicateArray.filter(
(item, index) => duplicateArray.indexOf(item) === index
);
Performance Advantages of the Map
Type
Map
is a key-value pair collection. Unlike plain objects, its keys can be of any type (including objects), not just strings or Symbols. The time complexity for lookup, insertion, and deletion operations in Map
is also O(1).
Lookup Performance Comparison
const obj = { key: 'value' };
const map = new Map([[key, 'value']]);
// Object lookup
console.time('Object property access');
console.log(obj.key); // 'value'
console.timeEnd('Object property access'); // ~0.05ms
// Map lookup
console.time('Map get');
console.log(map.get(key)); // 'value'
console.timeEnd('Map get'); // ~0.03ms
While the difference is negligible for small datasets, Map
's performance advantage becomes more noticeable as the number of keys increases:
const largeObj = {};
const largeMap = new Map();
for (let i = 0; i < 10000; i++) {
largeObj[i] = i;
largeMap.set(i, i);
}
// Object lookup
console.time('Large Object property access');
console.log(largeObj[9999]); // 9999
console.timeEnd('Large Object property access'); // ~0.1ms
// Map lookup
console.time('Large Map get');
console.log(largeMap.get(9999)); // 9999
console.timeEnd('Large Map get'); // ~0.03ms
Flexibility in Key Types
Map
allows objects to be used as keys, which is not possible with plain objects:
const objKey1 = { id: 1 };
const objKey2 = { id: 2 };
const map = new Map();
map.set(objKey1, 'value1');
map.set(objKey2, 'value2');
console.log(map.get(objKey1)); // 'value1'
Iteration Performance
Map
provides more efficient iteration methods, allowing direct traversal with for...of
:
const map = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
for (const [key, value] of map) {
console.log(key, value);
}
In contrast, iterating over an object's key-value pairs requires additional steps:
const obj = { key1: 'value1', key2: 'value2' };
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key]);
}
}
Memory Advantages of WeakSet
and WeakMap
WeakSet
and WeakMap
are variants of Set
and Map
that hold weak references to their keys, meaning they do not prevent garbage collection. This is particularly useful for memory management:
let obj = { data: 'important' };
const weakSet = new WeakSet();
weakSet.add(obj);
// When obj is no longer referenced, the entry in weakSet is automatically cleared
obj = null;
A typical use case for WeakMap
is storing private data:
const privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, { secret: 42 });
}
getSecret() {
return privateData.get(this).secret;
}
}
const instance = new MyClass();
console.log(instance.getSecret()); // 42
Practical Applications
Using Set
for Efficient Set Operations
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
// Union
const union = new Set([...setA, ...setB]); // Set {1, 2, 3, 4, 5}
// Intersection
const intersection = new Set([...setA].filter(x => setB.has(x))); // Set {3}
// Difference
const difference = new Set([...setA].filter(x => !setB.has(x))); // Set {1, 2}
Using Map
to Implement an LRU Cache
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.map = new Map();
}
get(key) {
if (!this.map.has(key)) return -1;
const value = this.map.get(key);
this.map.delete(key);
this.map.set(key, value);
return value;
}
put(key, value) {
if (this.map.has(key)) this.map.delete(key);
this.map.set(key, value);
if (this.map.size > this.capacity) {
this.map.delete(this.map.keys().next().value);
}
}
}
Browser Compatibility and Transpilation
While modern browsers support ES6 collection types, older browsers may require polyfills:
// Using core-js polyfills
import 'core-js/features/set';
import 'core-js/features/map';
For Babel users, necessary transpilation can be handled automatically via @babel/preset-env
:
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3,
},
],
],
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn