JavaScript Map vs Object (パフォーマンス編)
March 26, 2022 -前記事の続き
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 の方が速い。
Chrome(V8)
Safari(JSC)
Firefox(SM)
Edge(Chakra)
値の取得
m.get(x)
vs o[x]
こちらもいずれのブラウザでも Object の方がやや速い。
(このテストはランダム性があるので何回行ってみた)
Chrome(V8)
Safari(JSC)
Firefox(SM)
Edge(Chakra)
値の削除
m.delete(x)
vs delete o[x]
これは各ブラウザでばらつきがでた。Chrome,Edge だと delete メソッドの方が速いが、Safari だと delete オペレータの方が速い。Firefox はほぼ同じくらいだが map の方がやや速い。
Chrome(V8)
Safari(JSC)
Firefox(SM)
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];
}
}
- Chorme と Firefox,Edge はほぼ同じような結果になり、Safari は結構違う結果になった。
- value のみを利用したい場合は 9 の
Object.values
で変換し、昔ながらの for 文を使って走査するのが速い。 - 次点で
for(const v of Object.values(o))
- map ではネイティブのメソッドである
forEach
がどのブラウザでも速かった。 - Safari では map の forEach が圧倒的に早く、他の map, object の走査はどれも遅かった。
- map の forEach 以外だと、Object の
for..in
が次点で速かった。
- map の forEach 以外だと、Object の
結論としては、走査の書き方によって処理速度が顕著に変わる、map の場合は forEach
一択である。Object の場合は value 限定であれば、通常の for 文を利用するが一番速いが、バランスを取って for(const v of Object.values(o)){}
でもよいと思われる。
Chrome(V8)
Safari: map の forEach だけが異常に速く、それ以外はだいぶ遅い
Safari で map の forEach を除いた場合は、for..in が速かった
Firefox(SM): おおよそ Chrome と同じ
Edge(Chakra): こちらも Chrome, Firefox とおおよそ同じ
まとめ
Object と Map は同じ使い方ができるが、Map を使う場合の指標としては以下の点が上げられる
- キーに文字列(または Symbol)以外を指定したい場合は Map 一択となる
- プロパティの追加、削除が頻発する場合は、Map の方がパフォーマンスが上がる。
- 走査する場合、
Object.keys
やObject.values
を使わずに走査できるので、可読性が上がり、処理速度も速い。 - Object の要素数や空の判定も、もともと効率的な書き方ができなかったので、
size
メソッドによる恩恵も大きいと思う
JavaScript はもともと辞書型を定義するものが Object しかなかったので、仕方なくそれを使っていた面もあるので、機会(と要件)が合うのであれば積極的に Map も使って行きたい所である。