C++のunordered_mapを10の実例で完全ガイド

C++のunordered_mapを徹底解説するイメージC++
この記事は約15分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

C++プログラミングでは、データ構造はコードの効率と機能性を大きく左右します。

特にunordered_mapは、その柔軟性と高速なデータ処理能力により、C++開発者にとって不可欠なツールです。

この記事では、unordered_mapの基本から応用までを分かりやすく解説し、あなたがC++でのデータ管理をマスターできるようにします。

●unordered_mapとは

C++のunordered_mapは、キーと値のペアを格納するハッシュテーブルベースのコンテナです。

このデータ構造は標準ライブラリの一部であり、キーを使用して高速にデータを検索、挿入、削除することが可能です。

unordered_mapは、特にキーの重複がない状況で最大の効率を発揮します。

○unordered_mapの基本概念

unordered_mapは、キーと値のペアを格納し、キーに基づいてデータにアクセスします。

挿入、アクセス、削除という基本操作をサポートし、各操作はハッシュテーブルの利用により高速に行われます。

また、要素の数に応じて自動的にサイズが調整されるため、動的なデータ管理が可能です。

○unordered_mapの利点とは

unordered_mapの主要な利点は、その高速なデータアクセス能力にあります。

キーに基づく検索、挿入、削除は他の多くのデータ構造よりも速く行えます。

また、多様なデータタイプのキーと値を扱うことが可能で、ユーザー定義の型もサポートしています。

これにより、柔軟性が高く、さまざまな用途に適応できます。

○unordered_mapと他のデータ構造の比較

unordered_mapは、他のデータ構造と比較して一定の利点を有します。

例えば、std::mapは赤黒木に基づいており、要素がソートされた状態で保持されますが、unordered_mapはハッシュテーブルに基づいているため、一般的に検索や挿入の操作が高速です。

一方で、配列やベクターと比較すると、unordered_mapはキーによる直接アクセスが可能で、特に大規模なデータセットにおいて検索効率が優れています。

ただし、メモリの局所性の面では配列やベクターが有利です。

●unordered_mapの基本的な使い方

C++のunordered_mapは、非常に便利で強力なデータ構造です。

ここでは、unordered_mapを使用する基本的な方法について詳しく見ていきましょう。

unordered_mapの宣言から始め、要素の挿入、アクセス、削除の方法を順を追って説明します。

○サンプルコード1:unordered_mapの宣言と初期化

まず、unordered_mapを宣言し初期化する方法を見てみましょう。

下記のコードは、整数型のキーと文字列型の値を持つunordered_mapを宣言しています。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> map;
    map[1] = "apple";
    map[2] = "banana";

    std::cout << map[1] << std::endl;  // 出力: apple
    return 0;
}

このコードでは、まずunordered_mapを宣言し、整数のキーと文字列の値をペアとして追加しています。

そして、特定のキーを使って値にアクセスしています。

○サンプルコード2:要素の挿入とアクセス

次に、要素をunordered_mapに挿入し、それにアクセスする方法を見てみましょう。

下記のサンプルコードでは、いくつかの要素を追加し、それらをアクセスしています。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> map;
    map.insert(std::make_pair(3, "cherry"));
    map.insert(std::make_pair(4, "date"));

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

このコードでは、insertメソッドを使って新しいキーと値のペアをunordered_mapに追加しています。

その後、ループを使用してすべての要素にアクセスしています。

○サンプルコード3:要素の削除とサイズ管理

最後に、unordered_mapから要素を削除し、そのサイズを管理する方法を紹介します。

下記のコードでは、特定のキーを持つ要素を削除し、unordered_mapのサイズを確認しています。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> map;
    map[1] = "apple";
    map[2] = "banana";
    map[3] = "cherry";

    map.erase(2); // キー2を持つ要素を削除

    std::cout << "Size of map: " << map.size() << std::endl; // 出力: Size of map: 2
    return 0;
}

このコードでは、eraseメソッドを使用して特定のキーを持つ要素を削除しています。

その後、sizeメソッドを使用してunordered_mapの現在のサイズを出力しています。

●unordered_mapの応用例

C++のunordered_mapは基本的な使い方だけでなく、多岐にわたる応用例があります。

ここでは、カスタムデータタイプの使用、性能最適化のテクニック、イテレータを用いた要素の反復処理といった高度な使用方法を紹介します。

○サンプルコード4:カスタムデータタイプでの使用

unordered_mapは、標準のデータタイプだけでなく、カスタムデータタイプにも対応しています。

下記のコードは、カスタム構造体をキーとして使用する例を表しています。

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

struct MyKey {
    std::string key1;
    int key2;

    bool operator==(const MyKey &other) const {
        return (key1 == other.key1 && key2 == other.key2);
    }
};

struct MyHashFunction {
    size_t operator()(const MyKey &k) const {
        return std::hash<std::string>()(k.key1) ^ std::hash<int>()(k.key2);
    }
};

int main() {
    std::unordered_map<MyKey, std::string, MyHashFunction> myMap;
    MyKey key1 = {"Key1", 1};
    MyKey key2 = {"Key2", 2};

    myMap[key1] = "Value1";
    myMap[key2] = "Value2";

    std::cout << myMap[key1] << std::endl;  // 出力: Value1
    std::cout << myMap[key2] << std::endl;  // 出力: Value2

    return 0;
}

このコードでは、独自のハッシュ関数と等価関数を定義し、それを使用してunordered_mapをカスタマイズしています。

○サンプルコード5:性能最適化のためのテクニック

unordered_mapの性能を最適化するためには、適切なハッシュ関数の選択や、負荷係数の管理が重要です。

下記のコードは、負荷係数を調整して性能を最適化する方法を表しています。

#include <iostream>
#include <unordered_map>

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

    map.max_load_factor(0.25); // 負荷係数を下げる
    map.rehash(100); // バケットの数を増やす

    // 要素の追加
    for(int i = 0; i < 100; ++i) {
        map[i] = "value" + std::to_string(i);
    }

    std::cout << "Number of buckets: " << map.bucket_count() << std::endl;
    std::cout << "Load factor: " << map.load_factor() << std::endl;

    return 0;
}

このコードでは、max_load_factorrehash関数を用いて、ハッシュテーブルの負荷係数を調整し、バケットの数を増やしています。

○サンプルコード6:イテレータを用いた要素の反復処理

unordered_mapでは、イテレータを使用してコンテナ内の要素を反復処理することができます。

下記のコードは、イテレータを用いた要素の反復処理の例を表しています。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> map = {{1, "apple"}, {2, "banana"}, {3, "cherry"}};

    for (auto it = map.begin(); it != map.end(); ++it) {
        std::cout << it->first << ": " << it->second << std::endl;
    }

    return 0;
}

このコードでは、beginとend関数を使用して、unordered_mapの全要素をイテレータで走査しています。

各要素のキーと値にアクセスし、それらを出力しています。

●エラー処理とセキュリティ

C++のunordered_mapを使う際には、エラー処理とセキュリティが重要な側面となります。

正しくエラーをハンドリングし、セキュリティ上のリスクを最小限に抑えるための方法を見ていきましょう。

○サンプルコード7:エラーハンドリングの方法

unordered_mapでエラーを効果的にハンドリングするには、存在しないキーへのアクセスを避けることが重要です。

下記のコードは、エラーハンドリングの基本的な方法を表しています。

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

int main() {
    std::unordered_map<int, std::string> map = {{1, "apple"}, {2, "banana"}};

    int key = 3;
    if (map.find(key) == map.end()) {
        std::cout << "Key not found." << std::endl;
    } else {
        std::cout << "Value: " << map[key] << std::endl;
    }

    return 0;
}

このコードでは、findメソッドを使用してキーの存在をチェックしています。

キーが見つからない場合は、適切なメッセージを出力しています。

○サンプルコード8:unordered_mapのセキュリティ上の考慮事項

unordered_mapを安全に使用するためには、特にハッシュ関数の選択が重要です。

不適切なハッシュ関数は、パフォーマンスの問題やセキュリティの脆弱性を引き起こす可能性があります。

下記のコードは、カスタムハッシュ関数を安全に設計する方法を表しています。

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

struct SafeHash {
    std::size_t operator()(const int& key) const {
        return std::hash<int>()(key);
    }
};

int main() {
    std::unordered_map<int, std::string, SafeHash> map;
    map[1] = "apple";
    map[2] = "banana";

    // セキュアなハッシュ関数を使用
    std::cout << "Value: " << map[1] << std::endl;

    return 0;
}

このコードでは、標準のハッシュ関数をラップして、よりセキュアなハッシュ処理を行っています。

これにより、パフォーマンスの低下を防ぎつつ、セキュリティ上のリスクを軽減することができます。

●unordered_mapのカスタマイズ

C++におけるunordered_mapのカスタマイズは、特定のニーズに合わせてデータ構造の動作を最適化するために重要です。

カスタムハッシュ関数の作成やロードファクターとリサイズポリシーの調整など、いくつかの方法を通じてunordered_mapの挙動をカスタマイズすることができます。

○サンプルコード9:カスタムハッシュ関数の作成

unordered_mapのカスタマイズで最も一般的な方法の一つが、カスタムハッシュ関数の作成です。

下記のコードは、カスタムハッシュ関数を用いてunordered_mapをカスタマイズする方法を表しています。

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

struct CustomHash {
    size_t operator()(const std::string& key) const {
        size_t result = 0;
        for (char c : key) {
            result = result * 31 + c;
        }
        return result;
    }
};

int main() {
    std::unordered_map<std::string, int, CustomHash> map;
    map["apple"] = 1;
    map["banana"] = 2;

    std::cout << map["apple"] << std::endl;
    std::cout << map["banana"] << std::endl;

    return 0;
}

このコードでは、カスタムハッシュ関数CustomHashを定義し、unordered_mapに適用しています。

このカスタムハッシュ関数は、文字列キーに基づいてハッシュ値を計算します。

○サンプルコード10:ロードファクターとリサイズポリシーの調整

unordered_mapの性能を向上させるためには、ロードファクターとリサイズポリシーの調整も重要です。

下記のコードは、ロードファクターとリサイズポリシーを調整する方法を表しています。

#include <iostream>
#include <unordered_map>

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

    map.max_load_factor(0.5);
    map.reserve(20);

    for (int i = 0; i < 10; ++i) {
        map[i] = "value" + std::to_string(i);
    }

    std::cout << "Number of buckets: " << map.bucket_count() << std::endl;
    std::cout << "Size of map: " << map.size() << std::endl;
    std::cout << "Load factor: " << map.load_factor() << std::endl;

    return 0;
}

このコードでは、max_load_factor関数を用いてロードファクターを設定し、reserve関数を用いて事前にバケットの数を調整しています。

これにより、ハッシュテーブルの再ハッシュを減らし、パフォーマンスを向上させることが可能です。

まとめ

この記事を通じて、C++のunordered_mapの基本的な使い方から応用技術まで幅広く解説してきました。

エラーハンドリングからセキュリティの考慮、さらにはカスタマイズ方法まで、各セクションで示された詳細なサンプルコードは、実際のプログラミングにおいて非常に役立つでしょう。

unordered_mapはその柔軟性と効率性により、C++でのデータ管理において重要な役割を果たします。

これらの知識を活用し、より効果的なプログラムを作成してみてください。