kyohei's blog

profile picture
Written by Kyohei Tsukuda who lives and works in Tokyo 🇯🇵 , building useful things 🔧.
email / facebook / X / GitHub

JavaScript Map vs Object (パフォーマンス編)

March 26, 2022 - JavaScript Map Object

前記事の続き

JavaScript Map vs Object(実装編) | kyohei's blog

パフォーマンス

結局、どちらも同じようなことができることはわかったので、パフォーマンス的にどちらを利用するかを検証してみます。

Perflik を利用して、各処理のブラウザでのパフォーマンスを図ってみた。

(macOS Monterey)
Chrome Version 99
Safari Version 15.4
Firefox Version 98
(windows 10)
Edge Version 99

値の登録

m.set(x, y) vs o[x] = y

いずれのブラウザでも、Object の方が速い。

Perflink

Chrome(V8)

Chrome(V8)

Safari(JSC)

Safari(JSC)

Firefox(SM)

Firefox(SM)

Edge(Chakra)

Edge(Chakra)

値の取得

m.get(x) vs o[x]

こちらもいずれのブラウザでも Object の方がやや速い。

(このテストはランダム性があるので何回行ってみた)

Perflink

Chrome(V8)

Chrome(V8)

Safari(JSC)

Safari(JSC)

Firefox(SM)

Firefox(SM)

Edge(Chakra)

Edge(Chakra)

値の削除

m.delete(x) vs delete o[x]

これは各ブラウザでばらつきがでた。Chrome,Edge だと delete メソッドの方が速いが、Safari だと delete オペレータの方が速い。Firefox はほぼ同じくらいだが map の方がやや速い。

Perflink

Chrome(V8)

Chrome(V8)

Safari(JSC)

Safari(JSC)

Firefox(SM)

Firefox(SM)

Edge(Chakra)

Edge(Chakra)

走査

これはいくつかの操作のパターンを試してみた。

// 1. Map: for of パターン
for (const [k, v] of m) {
  sum += v;
}

// 2. Map: forEachメソッドパターン
m.forEach((v, k) => {
  sum += v;
});

// 3. Map: 配列に変換し、ArrayのforEachを使うパターン
[...m].forEach(([k, v]) => {
  sum += v;
});

// 4. Map: 配列に変換し、for文を使うパターン
let sum = 0;
const _m = [...m];
const len = _m.length;
for (let i = 0; i < len; i++) {
  sum += _m[i][1];
}

// 5. Object: Object.entriesでfor..ofを使うパターン
for (const [k, v] of Object.entries(o)) {
  sum += v;
}

// 6. Object: Object.values で for..ofを使うパターン
for (const v of Object.values(o)) {
  sum += v;
}

// 7. Object: Object.keys で for..ofを使うパターン
for (const k of Object.keys(o)) {
  sum += k;
}

// 8. Object: Object.values で ArrayのforEachを使うパターン
Object.values(o).forEach((v) => (sum += v));

// 9. Object: Object.values で for文を使うパターン
const vas = Object.values(o);
const len = vas.length;
for (let i = 0; i < len; i++) {
  sum += vas[i];
}

// 10. Object: for..inのパターン
for (const k in o) {
  if (o.hasOwnProperty(k)) {
    sum += o[k];
  }
}

結論としては、走査の書き方によって処理速度が顕著に変わる、map の場合は forEach 一択である。Object の場合は value 限定であれば、通常の for 文を利用するが一番速いが、バランスを取って for(const v of Object.values(o)){} でもよいと思われる。

Perflink リンク

Chrome(V8)

Chrome(V8)

Safari: mapのforEachだけが異常に速く、それ以外はだいぶ遅い

Safari: map の forEach だけが異常に速く、それ以外はだいぶ遅い

SafariでmapのforEachを除いた場合は、for..inが速かった

Safari で map の forEach を除いた場合は、for..in が速かった

Firefox(SM): おおよそChromeと同じ

Firefox(SM): おおよそ Chrome と同じ

Edge(Chakra): こちらもChrome, Firefoxとおおよそ同じ

Edge(Chakra): こちらも Chrome, Firefox とおおよそ同じ

まとめ

Object と Map は同じ使い方ができるが、Map を使う場合の指標としては以下の点が上げられる

JavaScript はもともと辞書型を定義するものが Object しかなかったので、仕方なくそれを使っていた面もあるので、機会(と要件)が合うのであれば積極的に Map も使って行きたい所である。