JavaScript | 第11章「キー付きコレクション」

javascrpit JavaScript
スポンサーリンク

「キー付きコレクション(Keyed Collections)」というのは、JavaScript における「キー(key)で値(value)を管理するデータ構造」の総称で、主に以下のものが含まれます:

  • Map
  • WeakMap
  • Set
  • WeakSet

初心者向けに「なぜこれらがあるのか」「どう使うのか」をステップごとに分かりやすく解説します。
「キー付きコレクション」ですが、「コレクション」は「複数の要素をまとめて扱う構造」という意味です。

1. なぜ “キー付きコレクション” が必要か

普通に JavaScript でデータを管理するとき、オブジェクト({ ... })を使って「キーと値」を対応づけることが多いです。しかしオブジェクトにはいくつか弱点があります:

問題点解決したい性質
キーは文字列かシンボルしか使えないobj[5] としたら実際には "5" という文字列キーになる任意の値(オブジェクト、配列、関数など)をキーにできたら便利
プロトタイプに由来するデフォルトのキーと衝突する可能性例えば toString など意図しないキーが混ざるのを防ぎたい
要素数を調べるのが面倒Object.keys(obj).length などを使わなければならないsize のように即座に数が得られたらいい
挿入順序を保証しないオブジェクトのキー列挙(for..in など)は挿入順とは限らない順序どおりに繰り返したい

これらを改善したものが「キー付きコレクション」です。たとえば Map は任意の型をキーにでき、要素数も map.size で即座に取得でき、挿入順序で反復できる、という利点があります。

2. Map(マップ)

Map は「キーと値の組を格納するコレクション」で、典型的な使い方は以下の通りです。 MDNウェブドキュメント

基本操作

const myMap = new Map();

// 値を設定(キーと値を対応づける)
myMap.set(key, value);

// 値を取得
myap.get(key);

// キーが存在するかチェック
myMap.has(key);

// キー‐値ペアを削除
myMap.delete(key);

// すべてクリア
myMap.clear();

// 要素数
myMap.size;
JavaScript

const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");

console.log(sayings.get("dog")); // "woof"
console.log(sayings.get("fox")); // undefined
console.log(sayings.has("bird")); // false

sayings.delete("dog");
console.log(sayings.has("dog")); // false

for (const [key, value] of sayings) {
  console.log(`${key}${value}`);
}
// 例の出力順:
// "cat → meow"
// "elephant → toot"
JavaScript

Object と Map の違い・どちらを使うか

  • Map のキーはどんな型でも使える(オブジェクト、関数、配列…など)一方、Object のキーは文字列またはシンボルに自動変換されます。
  • Mapsize で要素数がわかるが、Object だと自前で管理するか Object.keys(obj).length のようにする必要がある。
  • Map は挿入された順に反復できる。
  • Object はプロトタイプチェーンの影響で、意図しないキー(親オブジェクトのプロパティなど)が見えてしまうことがある。特別な用途では Object.create(null) を使ってプロトタイプを持たないオブジェクトを作ることもあります。

「どちらを使うべきか」はケースバイケースですが、以下のようなヒントがあります:

  • キー/値が実行時にしかわからないとき
  • キーにプリミティブ以外の型(オブジェクトなど)を使いたいとき
  • 要素数をすぐ取得したいとき
    …などの場合には Map が便利です。

3. WeakMap(ウィークマップ)

WeakMapMap に似ていますが、「弱い参照 (weak reference)」という特殊な性質があります。

特徴

  • WeakMap のキーは オブジェクトまたは非登録シンボル だけに限定されます(プリミティブ値は使えません)。
  • WeakMap はキーに対して強い参照 (strong reference) を持ちません。つまり、もしそのキーオブジェクトが他のところで参照されなくなれば、ガベージコレクション(不要なメモリ解放)される可能性があります。
  • 弱い参照を持つという性質上、WeakMap は列挙 (for…of などで全体を走査) ができません。なぜなら、いつガベージコレクションが発生するか不確定だからです。
  • WeakMap は、オブジェクトに「プライベートなデータ」を付随させる用途などに使われることがあります。例えば、クラスの外部からはアクセスさせたくない内部状態を WeakMap にキーを this にして格納しておく、というパターンがあります。

使い方例(簡略)

const privates = new WeakMap();

class MyClass {
  constructor(secret) {
    privates.set(this, { secretValue: secret });
  }

  reveal() {
    const data = privates.get(this);
    console.log(data.secretValue);
  }
}

const obj = new MyClass("秘密");
obj.reveal(); // "秘密"

 // obj が他で参照されなくなれば、
 // privates にある対応するエントリも自動的に解放される可能性がある
JavaScript

このように、プライベートなデータを外部から直接見えない形で保持する手段として WeakMap が使われます。

4. Set(セット)

Set は「値(value)のコレクション」で、重複を持たず挿入順で要素を反復できる構造です。

基本操作

const mySet = new Set();

mySet.add(1);
mySet.add("foo");
mySet.add(1); // すでに 1 があるので追加されない(重複しない)

console.log(mySet.has(1)); // true
mySet.delete("foo");
console.log(mySet.size); // 要素数

for (const item of mySet) {
  console.log(item);
}
JavaScript

配列との変換

SetArray の変換は簡単にできます:

const arr = [1, 2, 2, 3];
const s = new Set(arr); // {1, 2, 3}

const uniqueArr = Array.from(s); // [1, 2, 3]
const anotherArr = [...s];        // 同じく [1, 2, 3]
JavaScript

このように、配列から重複を取り除きたいときに Set を経由することがよくあります。

Set と Array の使い分け

  • 値が重複してはいけない、という保証を持たせたいときは Set が便利。
  • 特定の要素の追加・削除・検索を高速に行いたいときも Set は有利(ただし規模や利用頻度にもよる)。
  • 順序を保ちたい、かつ重複排除をしたい場面では Set が適している。

5. WeakSet(ウィークセット)

WeakSetSet の弱参照版、かつキー版とは少し異なる制約があります。

特徴

  • WeakSet に格納できる値は オブジェクトまたは非登録シンボル のみ。プリミティブ値(数値や文字列など)は格納できません。
  • WeakSet に入れたオブジェクトが他で参照されなくなれば、ガベージコレクションの対象になります(弱参照)。
  • WeakSet は列挙できません(どの要素が入っているか一覧で取得する API がありません)。これは、ガベージコレクションのタイミングが不確定なためです。

主な用途

DOM 要素のマーク、あるオブジェクトが「既に処理されたかどうか」を記録しておくなど、**オブジェクトに対して一時的に「フラグをつける」**用途で使われることがあります。

6. Map/Set における等価性の判定 ― SameValueZero アルゴリズム

Map(キー)と Set(値)で「同じかどうか」を判定する際には、JavaScript の厳密等価演算子 === に近いルールが使われますが、少し異なる点があります。MDN ではこれを “SameValueZero” アルゴリズムと呼んでいます。

  • 原則として === と同じ挙動をします。
  • ただし、-0+0 は等しいとみなされます。
  • NaN=== では等しくない(NaN === NaNfalse)ですが、SameValueZero では NaN は自身と等しいとみなされます。

つまり、Map のキーや Set の要素として NaN を使っても、正しく “存在” と “重複排除” が働きます。

7. まとめ(初心者向けのポイント整理)

  • 「キー付きコレクション」とは、キー(または値)で要素を管理する仕組みの総称で、Map, WeakMap, Set, WeakSet が含まれる。
  • Map はキーと値を対応づけて扱える、オブジェクトより高機能な構造。
  • WeakMap はキーがオブジェクト限定で、弱参照を使う(ガベージコレクションと連動する)もの。
  • Set は値のコレクションで、重複を許さず順序を保持する。
  • WeakSet はオブジェクト限定で弱参照を使う、列挙できない Set のようなもの。
  • 等価性の判定には “SameValueZero” というルールが使われ、NaN の扱いや +0 / −0 の扱いが === とは少し違う。
タイトルとURLをコピーしました