こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • C#のDictionaryって何?
  • Dictionaryの具体的な使い方を知りたい
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    DictionaryUnityでゲームを作るときに欠かせない機能です。Dictionary自体はレクションの1種で、 キーとセットで値を保持 します。

    「コレクション」や「キー」と言われてもよくわからない と思いますので詳しく解説します。 Dictionaryはゲーム開発では非常に便利な機能 なのでぜひ最後まで読んでマスターしてみてください。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    Dictionary(ディクショナリー)とは?

    Dictionaryとはキーと値をセットにしたコレクション です。そもそも「コレクション」とは複数のオブジェクトのグループ化し柔軟に処理するための機能。代表的なのは Listクラス です。配列と違い値を後から追加できます。Dictionaryも同様にコレクションの一種なのです。

    Dictionaryはキーを使って値を追加するのですが 「キー」とは何か?
    結論、 キーとはただの値です。 つまり値を使って値を取り出すのがDictionary。ただし、 キーの値はDictionaryの中でユニークである必要があります。 「ユニーク」とは重複しないということです。

    ユニークな値を使って、値を保持、取り出すことができるのがDictionaryなのです。
    Dictionaryの使い所の1つは、大量のオブジェクトを管理し、特定のオブジェクトにだけアクセスしたい場合です。例えば配列やListの場合、forやforeachを使って検索します。しかし、 Dictionaryはキーを使って直接アクセス するため検索負荷がかからず 高速に処理できる のです。

    DictionaryはListで代用できます。しかし、パフォーマンス的に大きく変わってくる場面があるため、ぜひ覚えておきたいテクニックです。特にゲーム開発では大量のオブジェクトを扱う場面が多く、Dictionaryが役立つ機会は数多くあります。

    Dictionaryの基本的な使い方4選

    ここからはDictionaryの基本的な使い方を紹介していきます。

    Dictionaryの基本的な使い方4選

    1.Dictionaryの宣言と生成

    2.Dictionaryに値の追加

    3.Dictionaryから値の取り出し

    4.Dictionary内の要素の削除

    使い方1.Dictionaryの宣言と生成

    Dictionaryはキーと値をセットで宣言し、「new」キーワードを作って生成するのです。

    文法はこちら。

    // Dictionaryの文法  
    Dictionary<キー,> 変数 = new Dictionary<キー,>();  
    

    Dictionaryのキーを数字、値を文字列にした場合、以下の通りになります。

    Dictionary<int, string> dic = new Dictionary<int, string>();  
    

    Dictionaryはどうしても宣言の文字数が多くなってしまうため、以下のとおり「var」を使って宣言することが多いです。

    var dic = new Dictionary<int, string>();  
    
    オオバ
    オオバ
    Dictionaryには「var」を使うことが多いです。

    使い方2.Dictionaryに値の追加

    では、Dictionaryに値を追加していきます。冒頭の解説のとおり、 Dictionaryは「キー」を使うコレクション です。つまり 値の追加は「キー」を使う のです。Dictionaryに値を追加する方法は4つありますので1つずつ解説していきます。

    その①Addメソッドで値の追加

    Addメソッドを使って値を追加していきます。

    var dic = new Dictionary<int, string>();  
    dic.Add(1, "渋谷");  
    dic.Add(2, "ほととぎす");  
    dic.Add(3, "通信");  
    

    注意点は、すでに登録済みのキーを使うと「エラーになることです。試しに以下のように同じキー「1」で値を追加してみます。

    var dic = new Dictionary<int, string>();  
    dic.Add(1, "渋谷");  
    dic.Add(1, "原宿"); // ← ここでエラー  
    

    すると以下のように ArgumentException というエラーがConsoleウィンドウに出力されます。

    ArgumentException: An item with the same key has already been added. Key: 1  
    

    その②TryAddで値の追加

    TryAddメソッドを使うと同じキーを使って値を追加してもエラーになりません。TryAddの戻り値はBool型。trueが返ってきたら追加成功、falseの場合はすでにキーが使われていて追加失敗となります。

    次の例のようにTryAddで処理の分岐が書けるのです。

    var dic = new Dictionary<int, string>();  
    dic.Add(1, "渋谷");  
    if (dic.TryAdd(1, "原宿"))  
    {
        // 追加成功  
    }
    else  
    {
        // 追加失敗  
    }
    

    その③添字を使った値の追加と上書き

    添字と配列やListで使う [] です。Dictionaryでも使うことができます。

    var dic = new Dictionary<int, string>();  
    dic[1] = "渋谷";  
    dic[2] = "ほととぎす";  
    dic[3] = "通信";  
    

    同じキーを使うと値を上書きできます。

    var dic = new Dictionary<int, string>();  
    dic[1] = "渋谷";  
    dic[1] = "原宿";  
    Debug.Log(dic[1]); // 出力 : 原宿  
    

    添字の場合は追加ではなく 上書き であることを覚えておきましょう。逆に言えば、 値を上書きするためには添字を使う必要がある ということです。

    その④初期化時に値の追加

    以下の例のようにDictionaryの初期化タイミングで値を追加することができます。

    var dic2 = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    

    最初からキーと値が決まっているものは、初期化タイミングでセットしておくのも手です。手段の1つとして覚えておきましょう。

    使い方3.Dictionaryから値の取り出し

    値を格納したDictionaryから値を取り出します。値を取り出すときも追加と同様「キー」を使います。

    その①添字で値の取り出し

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    var value = dic[1];  
    Debug.Log(value); // 出力 : 渋谷  
    

    注意点は存在しないキーを指定するとエラーだということです。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    var value = dic[4]; // 存在しないキー「4」で取得を試みる  
    

    上のサンプルは存在しないキー「4」を使って値を取り出そうとしています。すると以下のエラーが出力されます。

    KeyNotFoundException: The given key '4' was not present in the dictionary.  
    

    KeyNotFoundException というエラーが発生します。添字を使う場合は、以下のようにそのキーが存在するかチェックしてから使いましょう。

    if (dic.ContainsKey(4)) {  
        var value = dic[4];  
    }
    

    ContainsKeyメソッドを使うと、指定したキーがDictionary内に存在するかをチェックできます。

    その②TryGetValueを使った値の取り出し

    TryGetValueメソッド を使うと、添字を使った取得より安全にDictionaryから値を取得できます。次のサンプルコードを見てみましょう。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    if (dic.TryGetValue(2, out string value))  
    {
        // 値を取得できた場合  
        Debug.Log(value);  // 出力 : ほととぎす  
    }
    else  
    {
        // 値を取得できなかった場合  
        Debug.Log(value);  // 出力 : null  
    }
    

    このように取得できた場合はtrueを取得できなかった場合はfalseを返却します。
    TryGetValueの第2引数には注意が必要 です。変数宣言の手前に out キーワードを忘れずに記載しましょう。

    その③【Unity2021以降】GetOrDefaultを使った値の取り出し

    Unity2021以降限定の機能 ですが、 GetOrDefaultメソッド を使うとよりシンプルにDictionaryから値を取り出せます。

    var value = dic.GetOrDefault(1);  
    

    GetOrDefaultの引数にキーを代入すると、キーとセットの値が取得できます。

    GetOrDefaultの優れた点は、存在しないキーを選択してもエラーにならないこと。次のサンプルコードを見てみましょう。

    var value2 = dic.GetOrDefault(4);  
    Debug.Log(value); // 出力 : null  
    

    存在しないキーを指定してもエラーにならず、デフォルトの値(この場合null)が返ってくるのです。

    3種類Dictionaryから値を取得できますが、GetOrDefaultがシンプルで行数も少なくおすすめです。ただし Unity2021以降なので注意です。

    Unity2020以前の場合は、TryGetValueか、 GetOrDefaultに相当する拡張メソッドを自分で作るのもありだと思います。

    4.Dictionary内の要素の削除

    Dictionaryに追加した値を削除する方法を紹介します。削除ももちろんキーを使います。

    値の削除にはRemoveを使用します。引数にキーを渡すことで 値とキーどちらも削除します。

    dic.Remove(1);  
    

    Dictionaryの値削除の成功と失敗を判定可能

    Removeメソッドは戻り値がbool型です。削除できたらtrue、失敗したらfalseを返します。

    if (dic.Remove(4)) {  
        // 削除成功  
    } else {  
        // 削除失敗  
    }
    

    このような処理の分岐が可能です。

    Dictionaryの実践的な使い方

    ここからはより一層実践的なDictionaryの使い方を解説していきます。

    Dictionary内の要素数の取得

    Dictionaryの要素数はCountプロパティで取得できます。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    var count = dic.Count;  
    Debug.Log(count); // 出力 : 3  
    

    Dictionaryからすべてのキーと値の取り出し

    Dictionary内のすべてのキーと値を取り出す方法の紹介です。結論、foreachでDictionaryを回します。foreachでDictionaryを回すとKeyValuePair型が返ってくるのです。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    // foreachですべてのキーと値を取り出す  
    foreach (var kp in dic) {  
        var key = kp.Key;  
        var value = kp.Value;  
        Debug.Log($"{key} / {value}");  
    }
    

    KeyValuePairにKeyプロパティでキー、Valueプロパティで値を取得できます。

    KeyValuePairの代わりにvarを使う

    ところでKeyValuePairと書くのは面倒くさいです。たいてい場合 var を使うことが多いです。

    foreach (KeyValuePair<int, string> kp in dic)foreach (var kp in dic)  
    

    KeyValuePairの代わりにTuppleを使う方法

    また以下のようにTupple(タプル)を使って値を取得できます。

    foreach (var (key, value) in dic) {  
        Debug.Log($"{key} / {value}");  
    }
    

    すべてのキーだけを取り出す方法

    Dictionaryに追加されたすべてのキーだけを取得する方法を紹介。 Keysプロパティ を使います。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    // foreachですべてのキーを取り出す  
    foreach (var key in dic.Keys) {  
        // キーを取得  
        Debug.Log(key);  
    }
    

    DictionaryのKeysをforeachで回すことでキーを取得できます。

    すべての値だけを取り出す方法

    Dictionaryに追加されたすべての値だけを取得する方法を紹介。 Valuesプロパティ を使います。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    
    // foreachですべての値を取り出す  
    foreach (var value in dic.Values) {  
        // 値を取得  
        Debug.Log(value);  
    }
    

    DictionaryのValuesをforeachで回すことでキーを取得できます。

    Dictionaryに特定の値が存在するか確認する方法

    ContainesValueメソッド を使うと指定した値がDictionary内に存在するか確認できます。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    if (dic.ContainesValue("ほととぎす")) {  
        // 値が存在する  
    } else{  
        // 値は存在しなかった  
    }
    
    

    Dictionaryの値を削除と同時に値を取り出す方法

    Dictionaryから値を削除するときはRemoveメソッドを使いますが、 削除と同時に削除する値を取得する方法 があります。

    var dic = new Dictionary<int, string>()  
    {
        {1, "渋谷" }, {2, "ほととぎす" }, {3, "通信" },  
    };  
    dic.Remove(2, out var value);  
    Debug.Log(value); // 出力 : ほととぎす  
    

    このテクニックを使うと値を削除する前の値取得処理をスキップすることができます。記述コード量が減るのでおすすめです。

    UnityのDictionaryまとめ

    UnityのDictionaryについて解説してきました。簡単に記事の内容をまとめます。

    UnityのDictionaryまとめ

    ①Dictionaryはコレクションの1種である

    ②Dictionaryはキーと値をセットで格納する

    ③Dictionaryに追加、削除、取得はすべてキーを使う

    ④Dictionaryからすべての値を取得するときはforeachを使用する

    ⑤大量のオブジェクトの中から特定のオブジェクトにアクセスするときに活躍する

    こんな感じです。Dictionaryの使い方と仕様の解説だったため、実際にどう使ったら良いのかイメージつかなかったかもしれません。

    Dictionaryを使うタイミングは大量のオブジェクトの中から特定のオブジェクトにアクセスするとき です。もちろんListでも実装可能ですが、パフォーマンスに大きく影響を与える場合もあります。また、Listを使うよりコード量が減ることもありますので、処理の引き出しの1つとして覚えておきましょう。

    オススメ記事
    検証環境