JS에서 해시테이블은 대표적으로 Object, Map, Set이 있다. JS에서 key-value로 이루어진 자료구조는 Object가 대표적이였지만, ES6에서 Map과 Set이 추가되었다.
let map = new Map();
map.size;
map.clear();
map.get(key)
map.has(key)
map.set(key, value);
map.entries();
Object의 key는 기호나 문자만을 가질 수 있습니다. 하지만 Map은 어떠한 형태라도 key로 사용될 수 있습니다.
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
Object의 경우 일반적인 방법으로는 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은 설계단계부터 데이터의 추가와 제거에 최적화 되어 있기 때문에 성능에 있어서 매우 유리합니다.
맥북프로에서 천만개의 데이터 셋을 가지고 테스트 했을 때 Object는 1.6초의 처리시간이 필요했고 Map은 1ms 이하의 처리시간을 보였습니다.
Object를 사용하여 반복문을 실행시키려면 불편합니다. 하지만 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 이전 버전에서는 Object에서 키의 순서를 보장하지 않습니다.
Object에는 미리 정의된 prototype이 있기 때문에 key가 충돌할 위험이 있습니다. 하지만 Map을 사용할 때는 그런 걱정을 할 필요가 없습니다.
const map = new Map();
map.set('someKey1', 1);
map.set('someKey2', 2);
map.set('toString', 3); // No problem for Map
const plainObjMap = new Map();
plainObjMap['someKey1'] = 1;
plainObjMap['someKey2'] = 2;
plainObjMap['toString'] = 3; // Oops, native property