【C#】Dictionaryの使いかた

C#

ども、村正です!

今回は、C#のDictionary型について特徴や使いかたを紹介していきたいと思います。

Dictionaryとは?

Dictionaryはキーと値のペア(これを「エントリ」と呼びます)でデータを管理する構造になっています。

イメージとしては国語辞典のような感じです(そもそもDictionaryを訳すと辞書ですね…)。国語辞典には、ある言葉(例:リンゴ)に対して、その意味(例:ばら科の落葉高木。球体の甘酸っぱい赤い果物。(以下略))が書かれています。そのため”リンゴ”という単語が書かれた箇所を見ることで、その意味を知ることができます。

Dictionaryも同じ感じで、あるキー(例:string型の”リンゴ”)に値(例:string型の”ばら科の(以下略)”)を紐づけます。その状態のDictionaryに対してキーとして”リンゴ”を指定することで、そのキーに紐づいた値(リンゴの意味)にアクセスすることができます。

キーが重複していない(同じキーが存在しない)というのも忘れてはいけません。これは国語辞典に”リンゴ”という単語が書かれた項目が(基本的に)複数存在しないのと同じですね。

すこし長くなってしまいましたが、Dictionaryの特徴をまとめると以下の通りです。

  • 「”キー”と”値”」という単位でデータを管理する(この単位を一般的に「エントリ」という)。
  • キーを指定することで、そのキーに対応した値にアクセスできる。
  • キーは一意(同じ値は存在できない)であり、nullを指定できない。
  • 要素数を気にすることなくデータを追加・削除できる。
  • 値の取得・変更・追加・削除が非常に高速に行える(Oオーダー(1))。

配列・List・Dictionary の違い

配列、List、Dictionary の違いはデータ構造とアクセス速度にあります。

配列やListは、値をインデックス(添え字)で管理しています。numbers[7]のようにインデックス(7)を指定することで、そのインデックスの場所にある値を取得することができます。なお、配列とListの違いは「要素数が決まっているか」にあります。配列は宣言時に要素数を指定し、それ以降は要素数を増やすことはできません。しかしListの場合は要素数を柔軟に変更することができるため、要素数が決まっていない・わからない場合に使うと便利です。

対してDictionaryはキーと値のペアでデータを管理しています。キーは一意(重複禁止)でありnullを指定することはできませんが、値の追加/削除/変更/取得の処理を高速に(O(1))行うことができます。また要素数が決まっていないため、リストと同様に要素数を気にすることなくデータを増やすことができます。

ざっくりと特徴をまとめると以下の通りです。

エントリの持ちかた要素数値の取得要素の追加・削除(※)
配列インデックスで管理固定O(1):高速O(1):高速
※値の変更
Listインデックスで管理可変O(1):高速O(N):遅い
Dictionaryキーと値で管理可変O(1):高速O(1):高速

Dictionary の使いどころ

Dictionaryの使いどころ様々ですが、例えば「設定ファイルから読み込んだ値をプログラムで使うために保持しておく」といったときに使われます。

設定ファイルは一般的に「key=value」というような形で記載されています(例:.configファイル .iniファイル)。このような「項目名=値」のかたちでデータを記述している設定ファイルは、(ほぼ)そのままDictionaryのデータ構造に適用することができるのです。

Dictionary の使いかた

それではDictionaryの使いかたについて、実際のソースコードととも紹介していきたいと思います。

Dictionary(厳密にはDictionary<TKey, TValue>クラス)には多くのプロパティやメソッドがありますが、ここでは基本的な以下の5つについて見ていきます。

  • Dictionaryのインスタンスを作る方法
  • Dictionaryにエントリを追加する方法
  • Dictionaryからエントリを削除する方法
  • Dictionaryの値を取得する方法
  • Dictionaryの値を更新する方法

Dictionaryのインスタンスを作る方法

Dictionaryのインスタンスを作成するには、keyの型とvalueの型を指定する必要があります

public class Program
{
    static void Main(string[] args)
    {
        // key(string型), value(string型)を持つDictionaryの作成
        var keyValuePairs_01 = new Dictionary<string, string>();
    }
}

Dictionaryはkeyとvalueがジェネリック型になっています。そのため上記のように<string, string>とするとkeyとValueがともにstring型のDictionaryを作成でき、<string, int>とするとkeyがstring型でvalueがint型のDictionaryを作成できます。

また、Dictionaryには容量(要素数)が存在します。データを追加するとこの容量は自動的に増えていくためあまり気にする必要はありませんが、インスタンスを作成するときに初期容量を指定することができます。「最低でも100個の要素を持つことが分かっている」などの必要な容量があらかじめ分かっている場合は初期容量を指定しておくと、その容量までは要素の追加処理にて「容量を増やす処理」が発生しないためパフォーマンスが高まります。

public class Program
{
    static void Main(string[] args)
    {
        // 初期容量を指定することもできる(例:最低でも100個の要素を持つDictionaryであることが分かっている場合)
        var keyValuePairs_02 = new Dictionary<string, string>(100);
    }
}

Dictionaryにエントリを追加する方法

Dictionaryにエントリを追加するにはAddメソッド、もしくはTryAddメソッドか使います

メソッド戻り値説明
Add(key, value)-(void)Dictionaryにエントリを追加する。
すでに同じkeyが存在する場合は例外が発生する
※keyにnullを指定すると例外が発生する。
TryAdd(key, value)boolDictionaryにエントリを追加する。
すでに同じkeyが存在する場合はfalseが返ってくる。keyが存在せず追加が正常にできるとtrueが返ってくる。
※keyにnullを指定すると例外が発生する。
public class Program
{
    static void Main(string[] args)
    {
        // key(string型), value(string型)を持つDictionaryの作成
        var keyValuePairs = new Dictionary<string, string>();

        // エントリの追加
        keyValuePairs.Add("メロン", "Melon");
        keyValuePairs.TryAdd("りんご", "Apple");

        // エントリの一覧表示
        foreach (var i in keyValuePairs)
        {
            Console.WriteLine($"{i.Key} : {i.Value}");
        }
        /*
         * メロン : Melon
         * りんご : Apple
         */

        // TryAdd()を使ったエントリの追加(keyの重複 -> false が返ってくる)
        bool result_01 = keyValuePairs.TryAdd("メロン", "Merrrooooon");
        Console.WriteLine(result_01);
        /*
         * False
         */
        
        // Add()を使ったエントリの追加(keyの重複 -> ArgumentException 例外発生)
        keyValuePairs.Add("メロン", "Merrrooooon");
    }
}

Dictionaryからエントリを削除する方法

Dictionaryからエントリを削除するにはRemoveメソッドを使います。

メソッド戻り値説明
Remove(key)bool指定したキーを持つエントリをDictionaryから削除する。
削除できた場合はtrueを、削除できなかった場合はfalseを返す。
※keyにnullを指定すると例外が発生する。
public class Program
{
    static void Main(string[] args)
    {
        // key(string型), value(string型)を持つDictionaryの作成
        var keyValuePairs = new Dictionary<string, string>();

        // エントリの追加
        keyValuePairs.Add("メロン", "Melon");
        keyValuePairs.Add("りんご", "Apple");
        keyValuePairs.Add("バナナ", "Banana");

        // エントリの一覧表示
        foreach(var i in keyValuePairs)
        {
            Console.WriteLine($"{i.Key} : {i.Value}");
        }
        /*
         * メロン : Melon
         * りんご : Apple
         * バナナ : Banana
         */


        // エントリの削除(key="メロン"を持つエントリが存在するため成功 -> 戻り値:true)
        bool result_01 = keyValuePairs.Remove("メロン");

        // 削除結果の表示
        Console.WriteLine($"result_01 = {result_01}");
        foreach (var i in keyValuePairs)
        {
            Console.WriteLine($"{i.Key} : {i.Value}");
        }
        /*
         * result_01 = true
         * りんご : Apple
         * バナナ : Banana
         */

        // エントリの削除(key="みかん"を持つエントリが存在しないため失敗 -> 戻り値:false)
        bool result_02 = keyValuePairs.Remove("みかん");

        // 削除結果の表示
        Console.WriteLine($"result_02 = {result_02}");
        foreach (var i in keyValuePairs)
        {
            Console.WriteLine($"{i.Key} : {i.Value}");
        }
        /*
         * result_02 = false
         * りんご : Apple
         * バナナ : Banana
         */
    }
}

Dictionaryの値を取得・更新する方法

Dictinaryの値を取得・更新するにはインデクサプロパティを使用します。

「インデクサ」という言葉は聞き慣れない言葉かもしれませんが、なにも難しいことはありません。配列のときと同じように「変数名[key]」のように書くだけです。

プロパティ戻り値説明
インデクサ
this[key]
指定したkeyに紐づいた値にアクセスする。
Dictionaryに指定したkeyが存在しない場合は例外が発生する
※keyにnullを指定すると例外が発生する。
public class Program
{
    static void Main(string[] args)
    {
        // key(string型), value(string型)を持つDictionaryの作成
        var keyValuePairs = new Dictionary<string, string>();

        // エントリの追加
        keyValuePairs.Add("メロン", "Melon");
        keyValuePairs.TryAdd("りんご", "Apple");

        // 値の取得(key="メロン"の値を取得する)
        var value_01 = keyValuePairs["メロン"];

        // 値の変更(key="りんご"の値を変更する)
        keyValuePairs["りんご"] = "林檎";

        // 削除結果の表示
        foreach (var i in keyValuePairs)
        {
            Console.WriteLine($"{i.Key} : {i.Value}");
        }
        /*
         * メロン : Melon
         * りんご : 林檎
         */
    }
}

さいごに

今回はDictionaryについて紹介していきました。

  • 「”キー”と”値”」という単位でデータを管理する(この単位を一般的に「エントリ」という)。
  • キーを指定することで、そのキーに対応した値にアクセスできる。
  • キーは一意(同じ値は存在できない)であり、nullを指定できない。
  • 要素数を気にすることなくデータを追加・削除できる。
  • 値の取得・変更・追加・削除が非常に高速に行える(Oオーダー(1))。

Dictionaryには今回紹介した以外にも多くのプロパティやメソッドがあります。基本を押さえたらどんなものがあるのかをさっと見ておくと、スムーズにコーディングができるようになると思います。ぜひ公式ドキュメントで確認してみてくださいね。

コメント