JavaScriptのnew MapとArray.map()の違いとパフォーマンス比較

更新: 2025年7月14日

JavaScriptでデータを扱う際、new Map()とArray.map()は名前が似ていますが、全く異なる機能を持っています。本記事では、両者の基本的な違いから実際のパフォーマンス比較まで、開発現場で役立つ実践的な情報を詳しく解説します。

1. 基本的な違い

new Map()とは

new Map()は、キーと値のペアを格納するデータ構造です。ES6で導入されたオブジェクトの一種で、任意の値をキーとして使用できます。

// Mapの基本的な使用方法
const userMap = new Map();

userMap.set('name', 'John');

userMap.set('age', 30);

userMap.set(1, 'number key');

console.log(userMap.get('name')); // 'John'

console.log(userMap.size); //

Array.map()とは

Array.map()は、配列の各要素に対して関数を実行し、新しい配列を返すメソッドです。元の配列は変更されません。

// Array.map()の基本的な使用方法

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(x => x * 2);

console.log(doubled); // [2, 4, 6, 8, 10]

console.log(numbers); // [1, 2, 3, 4, 5] (元の配列は変更されない)

2. パフォーマンス比較データ

データ検索のパフォーマンス

実際のベンチマーク結果に基づくと、以下の特徴があります:

| 操作 | Map | Object | Array.find() |

|——|—–|——–|————-|

| 検索速度(大規模データ) | O(1) – 高速 | O(1) – 高速 | O(n) – 低速 |

| 挿入速度 | 高速 | 中速 | 低速 |

| 削除速度 | 高速 | 低速 | 低速 |

| メモリ使用量 | 多い | 少ない | 中程度 |

実際のベンチマーク結果

Chrome環境での測定結果:

// 大規模データセット(10万件)でのテスト結果

Map integer key set: 887ms

Object integer key set: 2,698ms

Array integer key set: 2,754ms

Map integer key get: 33ms

Object integer key get: 1,379ms

Array integer key get: 1,366ms

サイズ別パフォーマンス特性

**小規模データ(1,000件未満)**: ObjectとMapの差は僅か

**中規模データ(1,000-100,000件)**: MapがObjectの約2倍高速

**大規模データ(100,000件以上)**: Mapの優位性が顕著

3. メリット・デメリット比較

new Map()のメリット

1. **高速な検索・挿入・削除**

– ハッシュテーブル実装による高速アクセス

– 大規模データでも一定時間での操作

2. **柔軟なキータイプ**

const flexibleMap = new Map();

flexibleMap.set('string', 'value1');

flexibleMap.set(42, 'value2');

flexibleMap.set(true, 'value3');

flexibleMap.set({id: 1}, 'value4');

3. **順序保持**

– 挿入順序が保持される

– 反復処理で予測可能な順序

4. **サイズ取得が簡単**

console.log(myMap.size); // 簡単にサイズを取得

new Map()のデメリット

1. **メモリ使用量が多い**

– 1つのキーに対してObjectの約30倍のメモリ使用

– 4つのキーで約5倍のメモリ使用

2. **JSON直接変換不可**

// 直接変換できない

JSON.stringify(myMap); // "{}"

// 変換が必要

JSON.stringify(Array.from(myMap.entries()));

3. **学習コストが高い**

– 新しいAPIの習得が必要

– チーム内での知識共有が必要

Array.map()のメリット

1. **新しい配列を作成**

– 元の配列を変更しない

– 関数型プログラミングの原則に従う

2. **チェーンメソッドとの組み合わせ**

const result = numbers

.map(x => x * 2)

.filter(x => x > 10)

.reduce((sum, x) => sum + x, 0);

3. **直感的な使用方法**

– 配列変換処理が分かりやすい

– 開発者の理解が容易

Array.map()のデメリット

1. **パフォーマンスが劣る**

– forループに比べて処理速度が遅い

– 新しい配列を作成するためメモリ使用量が増加

2. **大規模データでの非効率性**

– 10万件以上のデータでは処理時間が長くなる

– メモリ使用量が元の配列の2倍になる

4. 使い分けのガイドライン

new Map()を使用するべき場面

1. **キーと値のペアを管理**

// ユーザーIDと詳細情報の管理

const userDetails = new Map();

userDetails.set('user123', {name: 'John', role: 'admin'});

2. **頻繁な検索・更新・削除**

// キャッシュシステムの実装

const cache = new Map();

function getDataWithCache(key) {

 if (cache.has(key)) {
   return cache.get(key);
 }

const data = fetchExpensiveData(key);

cache.set(key, data);

return data;

}

3. **非文字列キーが必要**

// オブジェクトをキーとして使用

const objectMap = new Map();

const userObject = {id: 1};

objectMap.set(userObject, 'user data');

Array.map()を使用するべき場面

1. **配列の各要素を変換**

// APIレスポンスのデータ変換

const users = apiResponse.map(user => ({

id: user.id,

displayName: `${user.firstName} ${user.lastName}`,

isActive: user.status === 'active'

}));

2. **関数型プログラミングのパターン**

// 複数のメソッドをチェーン

const processedData = rawData

.map(normalizeData)

.filter(isValid)

.map(formatForUI);

3. **小規模〜中規模データの処理**

// 1,000件未満のデータ変換

const formattedProducts = products.map(product => ({

...product,

price: formatPrice(product.price)

}));

5. 実践的なコード例

パフォーマンステスト

// パフォーマンステスト用のヘルパー関数

function performanceTest(testName, fn) {

const start = performance.now();

fn();

const end = performance.now();

console.log(`${testName}: ${end - start}ms`);

}

// 大規模データでのテスト

const largeDataSet = Array.from({length: 100000}, (_, i) => i);

// Map vs Object での検索テスト

const testMap = new Map();

const testObject = {};

largeDataSet.forEach(i => {

testMap.set(i, `value${i}`);

testObject[i] = `value${i}`;

});

// 検索テスト

performanceTest('Map search', () => {

for (let i = 0; i < 1000; i++) {

testMap.get(Math.floor(Math.random() * 100000));

}

});

performanceTest('Object search', () => {

for (let i = 0; i < 1000; i++) {

testObject[Math.floor(Math.random() * 100000)];

}

});

実際のアプリケーション例

// ユーザー管理システムの実装例

class UserManager {

constructor() {

this.users = new Map(); // 高速検索のためMapを使用

this.userIds = []; // 順序管理のため配列も併用

}

addUser(user) {

this.users.set(user.id, user);

this.userIds.push(user.id);

}

getUser(id) {

return this.users.get(id); // O(1)の高速検索

}

getAllUsers() {

return this.userIds.map(id => this.users.get(id)); // 順序保持

}

getActiveUsers() {

return this.userIds

.map(id => this.users.get(id))

.filter(user => user.isActive);

}

}

6. 注意点とベストプラクティス

メモリ管理

1. **Mapのメモリリーク防止**

// WeakMapを使用してメモリリークを防ぐ

const weakMap = new WeakMap();

function attachMetadata(object, metadata) {

weakMap.set(object, metadata);

}

2. **大規模データの処理**

// 大規模データではストリーミング処理を検討

function processLargeArray(array, processor) {

const chunkSize = 1000;

for (let i = 0; i < array.length; i += chunkSize) {

const chunk = array.slice(i, i + chunkSize);

chunk.map(processor);

}

}

パフォーマンス最適化

1. **適切なデータ構造の選択**

– 検索が多い:Map

– 変換が多い:Array.map()

– 小規模データ:Object

2. **混合使用の検討**

// MapとArrayの併用例

const dataMap = new Map(); // 高速検索用

const dataArray = []; // 順序保持・変換用

まとめ

new Map()とArray.map()は、それぞれ異なる用途に最適化されたJavaScriptの機能です。

**new Map()は以下の場合に最適**

– キーと値のペアを高速に管理したい

– 頻繁な検索・更新・削除が必要

– 大規模データを扱う

– 非文字列キーが必要

**Array.map()は以下の場合に最適**

– 配列の各要素を変換したい

– 関数型プログラミングのパターンを使用

– 小規模〜中規模データの処理

– 可読性とメンテナンス性を重視

パフォーマンスを重視する場合は、データサイズと操作頻度を考慮して適切に選択することが重要です。実際のアプリケーションでは、両方を組み合わせて使用することも多く、それぞれの特性を理解して適材適所で使い分けることが、効率的なJavaScriptコードの開発につながります。

Advertisement

広告スペース

728×90

Kentaro's Tech Blog

お問い合わせはこちら

プロジェクトのご相談、技術的な質問、コラボレーションのご提案など、 お気軽にお声がけください。

高品質な記事

実践的で分かりやすい技術記事を定期的に投稿しています。

WordPress + Next.js

モダンな技術スタックで構築された高速なブログサイトです。

継続的な更新

最新の技術トレンドに合わせて継続的にコンテンツを更新しています。