哈希表是日常编程中最常用的数据结构之一。它保存可以通过其 键
轻松访问 值
的键值对。在Java中很显然可以使用 HashMap
。在JavaScript中为了实现哈希表使用 Object
也非常方便。
// insert key-value-pair
map['key1'] = 'value1';
map['key2'] = 'value2';
map['key3'] = 'value3';
// check if map contians key
if (map['key1']) {
console.log('Map contains key1');
}
// get value with specific key
console.log(map['key1']);
但是JavaScript中有一个专门用于此目的的内置数据结构:Map。并且,Map结构相比Object有以下几个优势:
更多的键值类型
相比于对象的键值只能有 Symbol
或 String
类型。Map可以将任何类型的值作为键:对象,函数或基础类型。
const map = new Map();
const myFunction = () => console.log('I am a useful function.');
const myNumber = 666;
const myObject = {
name: 'plainObjectValue',
otherKey: 'otherValue'
};
map.set(myFunction, 'function as a key');
map.set(myNumber, 'number as a key');
map.set(myObject, 'object as a key');
console.log(map.get(myFunction)); // function as a key
console.log(map.get(myNumber)); // number as a key
console.log(map.get(myObject)); // object as a key
更方便确定大小
Map内部提供了 size
属性,可以方便获得map实例的大小。获取对象的大小却要绕一些远路。
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 1);
...
map.set('someKey100', 1);
console.log(map.size) // 100, Runtime: O(1)
const plainObjMap = {};
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 1;
...
plainObjMap['someKey100'] = 1;
console.log(Object.keys(plainObjMap).length) // 100, Runtime: O(n)
更优越的性能
基于上面的原因,在需要获取数据量大小的场景,Map拥有更好的性能,可以在常数时间获取map的大小,而对象却需要O(n)的时间。以使用Macbook Pro的示例为例,两种数据结构对一千万个条目确定大小的平均持续时间:
- 普通JS对象:1.6s
- Map: 1ms
更直接的迭代
必须通过获取密钥并对其进行迭代来迭代对象,但是Map可以直接进行迭代。
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 2);
map.set('someKey3', 3);
for (let [key, value] of map) {
console.log(`${key} = ${value}`);
}
// someKey1 = 1
// someKey2 = 2
// someKey3 = 3
const plainObjMap = {};
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 2;
plainObjMap['someKey3'] = 3;
for (let key of Object.keys(plainObjMap)) {
const value = plainObjMap[key];
console.log(`${key} = ${value}`);
}
// someKey1 = 1
// someKey2 = 2
// someKey3 = 3
键值有序
在ECMAScript 2015之前,不保证对象的键具有任何特定顺序。在Map上进行迭代可确保键按插入顺序出现。
无键值冲突
普通对象由于其原型已经包含一些属性。插入的键值和对象已包含的键之间可能存在冲突。Map在创建时不包含任何键。
注意:从ECMAScript 2015开始,可以通过使用
Object.create(null)
创建纯对象来避免意外的键覆盖。
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 2);
map.set('toString', 3); // No problem for Map
console.log(map.get("toString"));
const plainObjMap = new Map();
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 2;
plainObjMap['toString'] = 3;
console.log(plainObjMap.toString()); //TypeError: plainObjMap.toString is not a function