JavaScriptにおけるMap、Set、Objectの違いとは?
コンポーネントをリファクタリングしていて、キーと値のペアを保存する必要があるとします。習慣的にオブジェクトに手を伸ばしますが、ふと立ち止まります。これはMapにすべきでしょうか?Setの方が適しているでしょうか?この決定は見た目以上に重要です。各データ構造には、コードの明瞭性と効率性に影響を与える独自のパフォーマンス特性とセマンティックな意味があります。
このJavaScriptデータ構造比較では、本番コードで適切な選択ができるよう、Map、Set、Objectの実用的な違いを解説します。
重要なポイント
- Objectはキーを文字列に強制変換しますが、Mapはキーの型を正確に保持します。この違いにより、微妙な衝突バグを防ぐことができます。
- Mapは頻繁な追加と削除でObjectを上回るパフォーマンスを発揮しますが、Objectは安定した構造での読み取り中心のワークロードで優れています。
- SetはO(1)のメンバーシップチェックを提供し、現代のJavaScriptではネイティブの
union、intersection、differenceメソッドが搭載されています。 - 構造化データにはObjectをデフォルトとし、キーが動的または非文字列の場合はMapを選び、一意性が主な関心事の場合はSetを使用してください。
核心的な目的:Map vs Set vs Objectの使い分け
JavaScriptにおけるMapとObjectの違いは、意図とキーの扱い方に集約されます:
- Object: 既知の文字列/シンボルキーを持つ構造化データ。設定、コンポーネントのprops、APIレスポンスなどを想定。
- Map: キーが任意の型になり得る動的なキーと値のコレクション。キャッシュ、オブジェクトキーを持つルックアップテーブル、頻繁な追加と削除などを想定。
- Set: キーを持たない一意な値のコレクション。重複排除、確認済みアイテムの追跡、メンバーシップチェックなどを想定。
キーの扱い:MapとObjectの根本的な違い
Objectはキーを文字列に強制変換します。Mapはキーの型を正確に保持します。
const obj = {}
obj[1] = 'number'
obj['1'] = 'string'
console.log(Object.keys(obj)) // ['1'] — 衝突
const map = new Map()
map.set(1, 'number')
map.set('1', 'string')
console.log(map.size) // 2 — 異なるキー
Mapはオブジェクトをキーとして受け入れることもできます。これはObjectがシリアライゼーションの回避策なしには根本的にできないことです。
const userCache = new Map()
const user = { id: 42 }
userCache.set(user, { lastSeen: Date.now() })
完全なキーのセマンティクスについては、MapのMDNドキュメントを参照してください。
イテレーションと順序のセマンティクス
3つのデータ構造すべてに定義されたイテレーション順序がありますが、セマンティクスは異なります:
MapとSet: 純粋な挿入順序。常に。
Object: 文字列キーは挿入順序ですが、整数インデックスキー(例:'1'、'42')は数値的に最初にソートされます。これは開発者を驚かせることがあります:
const obj = { b: 1, 2: 2, a: 3, 1: 4 }
console.log(Object.keys(obj)) // ['1', '2', 'b', 'a']
MapとSetはfor...ofで直接イテレート可能です。ObjectはObject.keys()、Object.values()、またはObject.entries()が必要です。
大規模でのパフォーマンス特性
JavaScript Map vs Set vs Objectのパフォーマンスの問題については、コンテキストが重要です:
Objectは文字列キーでの読み取り中心のワークロードで優れています。V8のような最新エンジンは、隠しクラスを通じてプロパティアクセスを最適化し、オブジェクトの形状が安定している場合、読み取りを非常に高速にします。
Mapは頻繁な追加と削除で勝ります。オブジェクトのプロパティ削除(delete経由)は、隠しクラスを無効化することで最適化解除を引き起こす可能性があります。ECMAScript仕様は、特定の内部実装を義務付けることなく、Mapの平均アクセス時間が準線形であることを保証しています。
Setはhas()を介してO(1)のメンバーシップチェックを提供し、非自明なコレクションサイズの場合、配列のincludes()を上回ります。
Discover how at OpenReplay.com.
ネイティブなSet操作
Setには一般的な操作のための組み込みメソッドが含まれています(SetのMDNドキュメントを参照):
const a = new Set([1, 2, 3])
const b = new Set([2, 3, 4])
a.union(b) // Set {1, 2, 3, 4}
a.intersection(b) // Set {2, 3}
a.difference(b) // Set {1}
a.symmetricDifference(b) // Set {1, 4}
a.isSubsetOf(b) // false
これらのメソッドは最新のブラウザで広くサポートされています。
実践的なフロントエンドシナリオ
Objectを使用する場合:
- コンポーネントのpropsや設定を定義する場合
- JSONシリアライゼーションを扱う場合(Mapは直接シリアライズできません)
- キーが書き込み時に既知の場合
Mapを使用する場合:
- キーが動的またはユーザー提供の場合(プロトタイプ汚染を回避)
- オブジェクト参照をキーとして必要とする場合
- サイズ追跡が重要な場合(
map.sizevsObject.keys(obj).length) - 頻繁な更新を伴うキャッシュを構築する場合
Setを使用する場合:
- 一意なIDや確認済みアイテムを追跡する場合
- 配列の重複排除:
[...new Set(array)] - 高速なメンバーシップテスト
クイックリファレンス
| 機能 | Object | Map | Set |
|---|---|---|---|
| キーの型 | string、symbol | 任意 | N/A(値のみ) |
| サイズ | Object.keys().length | .size | .size |
| イテレーション | 間接的 | 直接 | 直接 |
| JSONサポート | ネイティブ | 手動 | 手動 |
| プロトタイプリスク | あり | なし | なし |
まとめ
Map、Set、Objectの選択は、どれが「より良い」かではなく、データ構造をユースケースに合わせることです。Objectは文字列キーを持つ構造化データに適した選択肢です。Mapは動的なキーと値のシナリオをクリーンに処理します。Setは一意性の問題を効率的に解決します。
シンプルさのためにObjectをデフォルトとしてください。特定の機能が必要な場合はMapを選んでください。一意性が主な関心事の場合はSetを使用してください。
よくある質問
直接はできません。JSON.stringifyはMapをネイティブに処理しません。まずArray.from(map)またはスプレッド演算子を使用してMapをエントリの配列に変換し、その配列を文字列化する必要があります。復元するには、JSONをパースし、結果の配列をMapコンストラクタに渡します:new Map(JSON.parse(jsonString))。
Mapは、オブジェクトのプロパティ削除が引き起こす可能性のある最適化解除なしに、頻繁な挿入と削除を処理します。また、sizeプロパティでネイティブにサイズを追跡し、DOMノードやオブジェクト参照を含む任意の値型をキーとして受け入れ、ユーザー提供のキーからのプロトタイプ汚染リスクがありません。
これらのメソッドは現在、最新のブラウザで広くサポートされています。古い環境をサポートする必要がある場合は、互換性テーブルを確認するか、ポリフィルを使用してください。
重複を含む挿入順序が重要な場合、インデックスベースのアクセスが必要な場合、またはコレクションがSetのオーバーヘッドを正当化できないほど小さい場合は、配列を使用してください。一意性と高速なルックアップが優先される大規模なコレクションの場合、Setがより良い選択です。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.