[Javascript] Map / Set / WeakMap / WeakSet
JS Map, JS Set, JS WeakMap, JS WeakSet, JS Map / Set
정의
ES6 의 4 가지 컬렉션. 객체 / 배열보다 적합한 경우.
| 컬렉션 | 키 | 값 | 강참조 |
|---|---|---|---|
Map | 모든 값 | 모든 값 | ✓ |
Set | (값만) | (값만) | ✓ |
WeakMap | 객체 only | 모든 값 | ✗ (약참조) |
WeakSet | 객체 only | (값만) | ✗ |
Map
const m = new Map();
m.set('a', 1);
m.set(42, 'number key');
m.set({}, 'object key'); // 객체도 키
m.set(true, 'bool key');
m.get('a'); // 1
m.has('a'); // true
m.delete('a');
m.size; // 3
m.clear();
// 초기화
const m2 = new Map([
['a', 1],
['b', 2],
]);
Map 의 순회
for (const [k, v] of m) { ... }
for (const k of m.keys()) { ... }
for (const v of m.values()) { ... }
m.forEach((v, k) => { ... });
// 배열로
[...m.entries()]
[...m.keys()]
[...m.values()]
삽입 순서 보존.
Map vs Object
| 항목 | Object | Map |
|---|---|---|
| 키 타입 | 문자열, Symbol | 모든 값 |
| 순서 보장 | 부분적 (string vs int) | ✓ (삽입 순서) |
| size | Object.keys(obj).length | .size |
| 순회 | for…in (prototype 주의) | for…of (자체만) |
| 기본 키 | prototype 키 (toString) | 없음 |
| 성능 (잦은 add/remove) | 느림 | 빠름 |
| JSON | ✓ | ✗ |
// Object 함정
const o = {};
o.toString; // function (prototype 에서)
// Map 안전
const m = new Map();
m.get('toString'); // undefined
Set
const s = new Set([1, 2, 3, 2, 1]); // {1, 2, 3}
s.add(4);
s.has(2); // true
s.delete(1);
s.size;
s.clear();
[...s]; // 배열로
자주 쓰는 패턴
// 배열 중복 제거
[...new Set([1, 2, 2, 3])] // [1, 2, 3]
// 합집합
new Set([...a, ...b])
// 교집합
new Set([...a].filter(x => b.has(x)))
// 차집합
new Set([...a].filter(x => !b.has(x)))
ES2025 에서 Set.prototype.union, intersection 등 메서드 추가.
WeakMap
const wm = new WeakMap();
let obj = {};
wm.set(obj, 'metadata');
obj = null; // obj 가 가비지 수집 → wm 의 entry 도 자동 제거
키가 객체만, 약참조. 객체가 GC 되면 entry 도 자동 제거.
용도
- 객체 메타데이터 : 객체 자체에 property 추가하지 않고 부가 정보 보관
- private state 흉내 : 외부에서 접근 못 함
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { password: 'secret' });
this.name = name;
}
checkPassword(p) {
return privateData.get(this).password === p;
}
}
#private 필드 (ES2022) 가 표준이지만 WeakMap 으로도 가능.
WeakSet
const ws = new WeakSet();
let obj = { id: 1 };
ws.add(obj);
ws.has(obj); // true
obj = null; // ws 에서 자동 제거
객체의 집합 (예: “처리된 객체”, “방문한 객체”).
WeakMap / WeakSet 제약
- 순회 메서드 없음 (
forEach,entries등) size없음clear()없음- 키는 객체만 (primitive 불가)
이유: 약참조 컬렉션은 어느 시점에 어떤 entry 가 살아있는지 알 수 없음.
자주 만나는 패턴
Map 으로 그룹화
const grouped = new Map();
for (const item of items) {
if (!grouped.has(item.category)) grouped.set(item.category, []);
grouped.get(item.category).push(item);
}
Set 으로 중복 체크
const seen = new Set();
for (const item of items) {
if (seen.has(item.id)) continue;
seen.add(item.id);
process(item);
}
WeakMap 으로 캐시
const cache = new WeakMap();
function expensiveCompute(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = doExpensive(obj);
cache.set(obj, result);
return result;
}
// obj 가 GC 되면 캐시도 자동 정리
함정
1. Map 의 키 비교
const m = new Map();
m.set({}, 1);
m.get({}); // undefined (다른 객체 참조)
const key = {};
m.set(key, 1);
m.get(key); // 1
객체 키는 참조 동등성.
2. NaN 키
const m = new Map();
m.set(NaN, 'x');
m.get(NaN); // 'x' (SameValueZero, NaN === NaN 처럼 취급)
3. JSON 으로 직렬화 안 됨
JSON.stringify(new Map([['a', 1]])) // '{}'
JSON.stringify(new Set([1, 2])) // '{}'
// 변환 필요
JSON.stringify([...map])
JSON.stringify({ ...Object.fromEntries(map) })
참고
- JS Object
- JS Array
- ConcurrentReferenceHashMap (개념적 유사: weak ref + map)
이 글의 용어 (3개)
- [Javascript] Arrayjavascript
- 정의 JavaScript 의 는 순서 있는 객체. 정수 키 + . 풍부한 메서드 (map/filter/reduce/...) 가 함수형 스타일을 지원. 생성 핵심 메서드 (반복) …
- [Javascript] Objectjavascript
- 정의 JavaScript 의 는 key-value 쌍의 컬렉션. 거의 모든 non-primitive 값이 객체. 함수, 배열, 정규식도 객체. 생성 접근 수정 / 삭제 Objec…
- [Spring] ConcurrentReferenceHashMapspring
- 정의 는 Spring Framework 의 핵심 유틸리티. 의 동시성 과 / 의 GC 친화성 을 결합한 Map. JDK 표준에 없는 조합. 메모리 압박 시 GC 가 entry 를…
💬 댓글