C++のstd::mapとstd::unordered_mapの違いと使い分けを徹底解説

C++のプログラムで効率的なデータ管理を行うためには、適切なコンテナを選ぶことが重要です。std::mapとstd::unordered_mapは、どちらもキーと値のペアを扱うためのコンテナですが、用途や性能においていくつかの違いがあります。本記事では、これらの違いを詳しく解説し、適切な使い分け方法について学びます。初心者から上級者まで役立つ情報を提供し、具体的な使用例やベストプラクティスも紹介します。

目次

std::mapの基本

std::mapは、キーと値のペアを扱う連想コンテナで、キーが自動的にソートされます。このコンテナは、キーに基づいて要素を迅速に検索、挿入、削除するための効率的な方法を提供します。内部的には赤黒木というバランスの取れた二分探索木を使用しており、要素の挿入、削除、検索の時間複雑度はO(log n)です。

std::mapの宣言と初期化

以下は、std::mapの基本的な宣言と初期化の例です。

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> myMap;
    myMap[1] = "One";
    myMap[2] = "Two";
    myMap[3] = "Three";

    for (const auto& pair : myMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

主な操作

std::mapで利用できる主な操作には、以下があります:

要素の挿入

新しい要素を追加するには、以下の方法を使用します。

myMap.insert(std::make_pair(4, "Four"));
myMap[5] = "Five";

要素の削除

特定のキーの要素を削除するには、以下の方法を使用します。

myMap.erase(2);

要素の検索

キーを使って要素を検索するには、以下の方法を使用します。

if (myMap.find(3) != myMap.end()) {
    std::cout << "Key 3 found: " << myMap[3] << std::endl;
} else {
    std::cout << "Key 3 not found." << std::endl;
}

これらの操作により、std::mapを使った効率的なデータ管理が可能になります。

std::unordered_mapの基本

std::unordered_mapは、キーと値のペアを扱う連想コンテナで、キーの順序付けは行われません。内部的にはハッシュテーブルを使用しており、要素の挿入、削除、検索の平均時間複雑度はO(1)です。このため、キーの順序が重要でない場合には、std::unordered_mapの方が高性能であることが多いです。

std::unordered_mapの宣言と初期化

以下は、std::unordered_mapの基本的な宣言と初期化の例です。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> myUnorderedMap;
    myUnorderedMap[1] = "One";
    myUnorderedMap[2] = "Two";
    myUnorderedMap[3] = "Three";

    for (const auto& pair : myUnorderedMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

主な操作

std::unordered_mapで利用できる主な操作には、以下があります:

要素の挿入

新しい要素を追加するには、以下の方法を使用します。

myUnorderedMap.insert(std::make_pair(4, "Four"));
myUnorderedMap[5] = "Five";

要素の削除

特定のキーの要素を削除するには、以下の方法を使用します。

myUnorderedMap.erase(2);

要素の検索

キーを使って要素を検索するには、以下の方法を使用します。

if (myUnorderedMap.find(3) != myUnorderedMap.end()) {
    std::cout << "Key 3 found: " << myUnorderedMap[3] << std::endl;
} else {
    std::cout << "Key 3 not found." << std::endl;
}

これらの操作により、std::unordered_mapを使った効率的なデータ管理が可能になります。ハッシュテーブルを使用するため、特に大規模なデータセットでの高性能が期待できます。

内部構造の違い

std::mapとstd::unordered_mapは、内部構造に大きな違いがあります。それぞれの内部構造は、異なる性能特性と用途を提供します。

std::mapの内部構造

std::mapは内部的に赤黒木(Red-Black Tree)を使用しています。これはバランスの取れた二分探索木の一種であり、すべての操作(挿入、削除、検索)がO(log n)の時間複雑度で実行されます。

赤黒木の特徴

  • バランスが保たれる: 木の高さが一定の範囲内に収まるため、操作の最悪時間複雑度がO(log n)に抑えられます。
  • 自動ソート: キーが挿入されると、自動的にソートされて木に配置されます。

赤黒木の利点

  • 順序維持: 要素が順序付けされて格納されるため、範囲ベースの操作や順序付けが必要な操作に適しています。
  • 一定の操作速度: 挿入、削除、検索の時間複雑度が一貫してO(log n)です。

std::unordered_mapの内部構造

std::unordered_mapはハッシュテーブルを使用しています。ハッシュテーブルでは、キーにハッシュ関数を適用して計算されたハッシュ値を使い、対応するバケットに要素を格納します。

ハッシュテーブルの特徴

  • 平均時間複雑度がO(1): 要素の挿入、削除、検索は平均的にO(1)の時間で実行されます。
  • 無順序: 要素はキーのハッシュ値に基づいて格納されるため、順序は保証されません。

ハッシュテーブルの利点

  • 高速操作: 特に大量のデータに対する操作が高速に行えます。
  • メモリ効率: 一部のシナリオでは、赤黒木に比べてメモリ使用量が少ない場合があります。

ハッシュ衝突とリハッシュ

ハッシュテーブルでは、異なるキーが同じバケットに格納されるハッシュ衝突が発生することがあります。これを処理するための一般的な方法は、チェイン法(バケットごとにリンクリストを使用)やオープンアドレッシング法(別のバケットに再配置)です。また、テーブルが一定の負荷率を超えると、再配置(リハッシュ)によってテーブルサイズが拡張され、パフォーマンスが維持されます。

これらの内部構造の違いにより、std::mapとstd::unordered_mapは異なる用途に適しています。順序が重要な場合や一定の操作速度が必要な場合はstd::map、速度重視で順序が不要な場合はstd::unordered_mapを選択するのが一般的です。

パフォーマンスの違い

std::mapとstd::unordered_mapのパフォーマンスは、内部構造の違いによって大きく異なります。ここでは、挿入、削除、検索の各操作におけるパフォーマンスの違いを具体的に比較します。

挿入操作のパフォーマンス

  • std::map: 挿入操作はO(log n)の時間複雑度です。これは赤黒木のバランスを維持するための再配置が必要だからです。
  • std::unordered_map: 挿入操作は平均してO(1)の時間複雑度です。ただし、ハッシュ衝突やリハッシュが発生すると、最悪の場合O(n)になることもあります。

挿入操作の例

以下は、std::mapとstd::unordered_mapで同じ要素を挿入するコード例です。

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>

int main() {
    std::map<int, int> orderedMap;
    std::unordered_map<int, int> unorderedMap;

    // std::mapへの挿入
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        orderedMap[i] = i;
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "std::map insertion: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    // std::unordered_mapへの挿入
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        unorderedMap[i] = i;
    }
    end = std::chrono::high_resolution_clock::now();
    std::cout << "std::unordered_map insertion: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    return 0;
}

削除操作のパフォーマンス

  • std::map: 削除操作もO(log n)の時間複雑度です。赤黒木の再配置が必要なためです。
  • std::unordered_map: 削除操作は平均してO(1)の時間複雑度です。ただし、ハッシュ衝突の処理やリハッシュが絡むと、最悪の場合O(n)になります。

削除操作の例

以下は、std::mapとstd::unordered_mapから同じ要素を削除するコード例です。

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>

int main() {
    std::map<int, int> orderedMap;
    std::unordered_map<int, int> unorderedMap;

    for (int i = 0; i < 1000000; ++i) {
        orderedMap[i] = i;
        unorderedMap[i] = i;
    }

    // std::mapからの削除
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        orderedMap.erase(i);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "std::map deletion: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    // std::unordered_mapからの削除
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        unorderedMap.erase(i);
    }
    end = std::chrono::high_resolution_clock::now();
    std::cout << "std::unordered_map deletion: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    return 0;
}

検索操作のパフォーマンス

  • std::map: 検索操作はO(log n)の時間複雑度です。これは赤黒木で要素を探索するための時間です。
  • std::unordered_map: 検索操作は平均してO(1)の時間複雑度です。ただし、ハッシュ衝突が発生すると、最悪の場合O(n)になることもあります。

検索操作の例

以下は、std::mapとstd::unordered_mapから同じ要素を検索するコード例です。

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>

int main() {
    std::map<int, int> orderedMap;
    std::unordered_map<int, int> unorderedMap;

    for (int i = 0; i < 1000000; ++i) {
        orderedMap[i] = i;
        unorderedMap[i] = i;
    }

    // std::mapでの検索
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        auto it = orderedMap.find(i);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "std::map search: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    // std::unordered_mapでの検索
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 1000000; ++i) {
        auto it = unorderedMap.find(i);
    }
    end = std::chrono::high_resolution_clock::now();
    std::cout << "std::unordered_map search: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;

    return 0;
}

これらのコード例を通じて、std::mapとstd::unordered_mapのパフォーマンスの違いを実感できます。適切なコンテナを選択することで、プログラムの性能を大幅に向上させることが可能です。

メモリ使用量の比較

std::mapとstd::unordered_mapのメモリ使用量は、それぞれの内部構造によって異なります。この違いを理解することで、メモリ効率の良いコンテナを選択することができます。

std::mapのメモリ使用量

std::mapは内部に赤黒木を使用しており、各ノードは以下の情報を持ちます:

  • キーと値のペア
  • 左右の子ノードへのポインタ
  • 親ノードへのポインタ
  • カラー情報(赤または黒)

このため、std::mapは各要素に対して追加のポインタを複数保持しているため、メモリ使用量が多くなる傾向があります。

メモリ使用量の例

以下は、std::mapのメモリ使用量を確認するためのコード例です。

#include <iostream>
#include <map>
#include <chrono>
#include <cstdlib>

int main() {
    std::map<int, int> orderedMap;

    // 100万個の要素を挿入
    for (int i = 0; i < 1000000; ++i) {
        orderedMap[i] = rand();
    }

    std::cout << "std::map size: " << orderedMap.size() << std::endl;

    return 0;
}

std::unordered_mapのメモリ使用量

std::unordered_mapは内部にハッシュテーブルを使用しており、バケット(配列)の各要素は以下の情報を持ちます:

  • キーと値のペア
  • 次の要素へのポインタ(ハッシュ衝突が発生した場合)

ハッシュテーブルのサイズは動的に変化し、負荷率(ロードファクター)に基づいてリハッシュされます。これにより、初期のメモリ使用量は少ないですが、リハッシュの際に一時的にメモリ使用量が増加します。

メモリ使用量の例

以下は、std::unordered_mapのメモリ使用量を確認するためのコード例です。

#include <iostream>
#include <unordered_map>
#include <chrono>
#include <cstdlib>

int main() {
    std::unordered_map<int, int> unorderedMap;

    // 100万個の要素を挿入
    for (int i = 0; i < 1000000; ++i) {
        unorderedMap[i] = rand();
    }

    std::cout << "std::unordered_map size: " << unorderedMap.size() << std::endl;

    return 0;
}

メモリ使用量の比較

std::mapとstd::unordered_mapのメモリ使用量を直接比較するには、以下のポイントを考慮する必要があります:

  • 構造のオーバーヘッド: std::mapはノードのポインタオーバーヘッドが多いため、同じ要素数であってもstd::unordered_mapより多くのメモリを使用します。
  • バケットのサイズ: std::unordered_mapはバケットのサイズと負荷率に依存するため、リハッシュが頻繁に発生する場合、一時的に多くのメモリを消費します。

比較結果の考察

一般的に、キーの順序が必要な場合やメモリ使用量があまり重要でない場合はstd::mapを使用し、高速な操作とメモリ効率を重視する場合はstd::unordered_mapを選択するのが適しています。メモリ使用量とパフォーマンスのバランスを考慮して、最適なコンテナを選択することが重要です。

使用例とベストプラクティス

std::mapとstd::unordered_mapを効果的に使い分けるための具体的な使用例とベストプラクティスを紹介します。これにより、各コンテナの利点を最大限に活用できるようになります。

std::mapの使用例とベストプラクティス

使用例1: 順序付けられたデータの管理

キーが自動的にソートされるため、std::mapは順序付けが必要なデータ管理に適しています。例えば、学生の成績を管理する場合:

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> studentGrades;
    studentGrades[90] = "A";
    studentGrades[80] = "B";
    studentGrades[70] = "C";

    for (const auto& pair : studentGrades) {
        std::cout << "Score: " << pair.first << ", Grade: " << pair.second << std::endl;
    }

    return 0;
}

使用例2: 範囲ベースの操作

std::mapはキーがソートされているため、範囲ベースの操作が簡単に行えます。例えば、特定の範囲内の成績を取得する場合:

auto itLow = studentGrades.lower_bound(80);
auto itHigh = studentGrades.upper_bound(90);

for (auto it = itLow; it != itHigh; ++it) {
    std::cout << "Score: " << it->first << ", Grade: " << it->second << std::endl;
}

ベストプラクティス

  • 挿入時に順序が必要な場合: std::mapを選択することで、常にキーがソートされている状態を保てます。
  • 範囲検索を多用する場合: std::mapの範囲ベースの操作を活用すると効率的です。

std::unordered_mapの使用例とベストプラクティス

使用例1: 高速なデータ検索

ハッシュテーブルを使用するため、std::unordered_mapは高速なデータ検索に適しています。例えば、ユーザーIDとその詳細を管理する場合:

#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    std::unordered_map<int, std::string> userDetails;
    userDetails[1001] = "Alice";
    userDetails[1002] = "Bob";
    userDetails[1003] = "Charlie";

    int userId = 1002;
    if (userDetails.find(userId) != userDetails.end()) {
        std::cout << "User ID: " << userId << ", Name: " << userDetails[userId] << std::endl;
    } else {
        std::cout << "User ID: " << userId << " not found." << std::endl;
    }

    return 0;
}

使用例2: 順序が不要なデータ管理

順序が重要でない場合には、std::unordered_mapを使うことで効率的にデータを管理できます。例えば、商品IDと在庫数の管理:

std::unordered_map<int, int> productInventory;
productInventory[101] = 50;
productInventory[102] = 20;
productInventory[103] = 15;

ベストプラクティス

  • 高速な挿入と検索: 順序が不要な場合はstd::unordered_mapを選択してパフォーマンスを向上させます。
  • ハッシュ関数の選択: カスタムオブジェクトをキーにする場合は、適切なハッシュ関数を提供することが重要です。

まとめ

std::mapとstd::unordered_mapの使用例とベストプラクティスを理解することで、適切なシチュエーションでそれぞれのコンテナを選択し、効率的なプログラムを書くことができます。データの順序付けが必要な場合や範囲検索を多用する場合はstd::mapを、パフォーマンス重視で順序が不要な場合はstd::unordered_mapを使用するのがベストです。

応用例:特定のケースでの使い分け

特定のプログラムシナリオにおいて、std::mapとstd::unordered_mapのどちらを使うべきかについての具体的な例を紹介します。これにより、各コンテナの特性を理解し、適切な選択をするための参考になります。

ケース1: 順序が重要なデータの管理

シナリオ: イベントのタイムラインを管理するアプリケーションを開発する場合、イベントを発生した順序に従って処理する必要があります。このような場合には、std::mapが適しています。

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> eventTimeline;
    eventTimeline[1] = "Event A";
    eventTimeline[3] = "Event C";
    eventTimeline[2] = "Event B";

    std::cout << "Event Timeline:" << std::endl;
    for (const auto& event : eventTimeline) {
        std::cout << "Time: " << event.first << ", Event: " << event.second << std::endl;
    }

    return 0;
}

解説: std::mapを使用すると、イベントはキー(時間)に基づいて自動的にソートされるため、タイムラインの順序を簡単に管理できます。

ケース2: 高速なデータ検索が重要

シナリオ: ユーザー認証システムでは、ユーザーIDからユーザー情報を高速に検索する必要があります。この場合、std::unordered_mapが適しています。

#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    std::unordered_map<int, std::string> userAuth;
    userAuth[1001] = "UserA";
    userAuth[1002] = "UserB";
    userAuth[1003] = "UserC";

    int userId = 1002;
    if (userAuth.find(userId) != userAuth.end()) {
        std::cout << "User ID: " << userId << ", Name: " << userAuth[userId] << std::endl;
    } else {
        std::cout << "User ID: " << userId << " not found." << std::endl;
    }

    return 0;
}

解説: std::unordered_mapはハッシュテーブルを使用しているため、ユーザーIDからユーザー情報を平均O(1)の時間で高速に検索できます。

ケース3: 重複キーの管理が必要

シナリオ: ショッピングカートのアイテムを管理する場合、同じ商品が複数回追加されることがあります。重複キーを許容する必要があるため、この場合はstd::multimapやstd::unordered_multimapを使用することが考えられます。

#include <iostream>
#include <map>
#include <string>

int main() {
    std::multimap<int, std::string> shoppingCart;
    shoppingCart.insert(std::make_pair(101, "Apple"));
    shoppingCart.insert(std::make_pair(102, "Banana"));
    shoppingCart.insert(std::make_pair(101, "Apple"));

    std::cout << "Shopping Cart:" << std::endl;
    for (const auto& item : shoppingCart) {
        std::cout << "Product ID: " << item.first << ", Product: " << item.second << std::endl;
    }

    return 0;
}

解説: std::multimapを使用すると、同じキー(商品ID)を複数回挿入できるため、重複キーの管理が簡単に行えます。

ケース4: メモリ使用量の最適化が必要

シナリオ: 組み込みシステムやメモリが限られた環境では、メモリ使用量を最小限に抑える必要があります。ここでは、要件に応じてstd::mapまたはstd::unordered_mapを選択します。

#include <iostream>
#include <map>
#include <unordered_map>

int main() {
    std::map<int, int> orderedMap;
    std::unordered_map<int, int> unorderedMap;

    for (int i = 0; i < 1000; ++i) {
        orderedMap[i] = i;
        unorderedMap[i] = i;
    }

    std::cout << "std::map size: " << orderedMap.size() << std::endl;
    std::cout << "std::unordered_map size: " << unorderedMap.size() << std::endl;

    return 0;
}

解説: メモリ使用量を測定し、特定のシナリオにおけるメモリ効率を比較して、より適したコンテナを選択します。

これらの具体例を通じて、std::mapとstd::unordered_mapの適切な使い分けが理解できるでしょう。プログラムの要件に応じて、最適なコンテナを選択することが重要です。

演習問題

これまで学んだstd::mapとstd::unordered_mapの違いや使い分けを実践的に理解するために、以下の演習問題に挑戦してみましょう。各問題の後に解説も含めますので、自分で考えた後に解説を確認してください。

演習問題1: イベントスケジューラの作成

イベントスケジューラを作成し、イベントを追加、削除、検索するプログラムを実装してください。イベントは時刻(整数)とイベント名(文字列)のペアで管理されます。時刻順にソートされた状態を保つ必要があります。

問題: 以下の機能を持つイベントスケジューラを実装しなさい。

  • イベントの追加
  • イベントの削除
  • 時刻を指定してイベントを検索
#include <iostream>
#include <map>
#include <string>

class EventScheduler {
public:
    void addEvent(int time, const std::string& event) {
        events[time] = event;
    }

    void removeEvent(int time) {
        events.erase(time);
    }

    void findEvent(int time) const {
        auto it = events.find(time);
        if (it != events.end()) {
            std::cout << "Event at " << time << ": " << it->second << std::endl;
        } else {
            std::cout << "No event found at " << time << std::endl;
        }
    }

    void displayAllEvents() const {
        for (const auto& event : events) {
            std::cout << "Time: " << event.first << ", Event: " << event.second << std::endl;
        }
    }

private:
    std::map<int, std::string> events;
};

int main() {
    EventScheduler scheduler;
    scheduler.addEvent(10, "Meeting with Bob");
    scheduler.addEvent(14, "Lunch with Alice");
    scheduler.addEvent(16, "Conference Call");

    std::cout << "All Events:" << std::endl;
    scheduler.displayAllEvents();

    std::cout << "\nFind Event at 14:" << std::endl;
    scheduler.findEvent(14);

    std::cout << "\nRemove Event at 10:" << std::endl;
    scheduler.removeEvent(10);
    scheduler.displayAllEvents();

    return 0;
}

解説: このプログラムでは、std::mapを使用してイベントを時刻順にソートして管理しています。追加、削除、検索の操作も簡単に行えます。

演習問題2: ユーザー認証システムの作成

ユーザーIDとパスワードを管理する簡単なユーザー認証システムを作成し、ユーザーの追加、削除、認証を行うプログラムを実装してください。ユーザーIDは一意であり、順序は重要ではありません。

問題: 以下の機能を持つユーザー認証システムを実装しなさい。

  • ユーザーの追加
  • ユーザーの削除
  • ユーザー認証(ユーザーIDとパスワードのペアを確認)
#include <iostream>
#include <unordered_map>
#include <string>

class UserAuthSystem {
public:
    void addUser(int userId, const std::string& password) {
        users[userId] = password;
    }

    void removeUser(int userId) {
        users.erase(userId);
    }

    bool authenticate(int userId, const std::string& password) const {
        auto it = users.find(userId);
        if (it != users.end() && it->second == password) {
            std::cout << "Authentication successful for User ID: " << userId << std::endl;
            return true;
        } else {
            std::cout << "Authentication failed for User ID: " << userId << std::endl;
            return false;
        }
    }

private:
    std::unordered_map<int, std::string> users;
};

int main() {
    UserAuthSystem authSystem;
    authSystem.addUser(1001, "password123");
    authSystem.addUser(1002, "mypassword");
    authSystem.addUser(1003, "abc123");

    std::cout << "Authenticate User 1002:" << std::endl;
    authSystem.authenticate(1002, "mypassword");

    std::cout << "\nRemove User 1001 and Authenticate:" << std::endl;
    authSystem.removeUser(1001);
    authSystem.authenticate(1001, "password123");

    return 0;
}

解説: このプログラムでは、std::unordered_mapを使用してユーザーIDとパスワードを管理しています。ハッシュテーブルの特性を活かして高速な検索と認証を実現しています。

演習問題3: 商品在庫管理システムの作成

商品IDと在庫数を管理する在庫管理システムを作成し、商品を追加、削除、在庫数を更新するプログラムを実装してください。商品IDは一意であり、順序は重要ではありません。

問題: 以下の機能を持つ在庫管理システムを実装しなさい。

  • 商品の追加
  • 商品の削除
  • 在庫数の更新
  • 商品の在庫数を表示
#include <iostream>
#include <unordered_map>

class InventorySystem {
public:
    void addProduct(int productId, int quantity) {
        inventory[productId] = quantity;
    }

    void removeProduct(int productId) {
        inventory.erase(productId);
    }

    void updateQuantity(int productId, int quantity) {
        inventory[productId] = quantity;
    }

    void displayInventory() const {
        for (const auto& product : inventory) {
            std::cout << "Product ID: " << product.first << ", Quantity: " << product.second << std::endl;
        }
    }

private:
    std::unordered_map<int, int> inventory;
};

int main() {
    InventorySystem inventorySystem;
    inventorySystem.addProduct(101, 50);
    inventorySystem.addProduct(102, 20);
    inventorySystem.addProduct(103, 15);

    std::cout << "Initial Inventory:" << std::endl;
    inventorySystem.displayInventory();

    std::cout << "\nUpdate Quantity of Product 102:" << std::endl;
    inventorySystem.updateQuantity(102, 30);
    inventorySystem.displayInventory();

    std::cout << "\nRemove Product 101:" << std::endl;
    inventorySystem.removeProduct(101);
    inventorySystem.displayInventory();

    return 0;
}

解説: このプログラムでは、std::unordered_mapを使用して商品IDと在庫数を管理しています。高速な挿入、削除、更新操作を実現しています。

これらの演習問題を通じて、std::mapとstd::unordered_mapの違いや使い分けを深く理解できるでしょう。実際のプログラミングに役立ててください。

まとめ

C++のstd::mapとstd::unordered_mapは、それぞれ異なる特性と用途を持つ強力なコンテナです。std::mapはキーの順序を保ちながら効率的にデータを管理し、範囲ベースの操作に適しています。一方、std::unordered_mapはハッシュテーブルを使用しており、高速な挿入、削除、検索操作を提供します。

用途に応じてこれらのコンテナを使い分けることで、プログラムの性能とメモリ効率を最適化することが可能です。具体的な使用例や演習問題を通じて、それぞれのコンテナの特性を理解し、適切な場面での活用方法を学ぶことができました。

データの順序付けが必要な場合や範囲検索を多用する場合はstd::mapを選択し、速度重視で順序が不要な場合はstd::unordered_mapを使用することが最適です。今後のプログラミングにおいて、これらのコンテナを効果的に活用していきましょう。

コメント

コメントする

目次