配列やListと並んで使われるDictionaryですが、実は用途に応じていくつかの種類が存在します。
今回は、C#で使えるDictionaryの種類と使い道について解説していきます。
用途に応じて使い分けることが大切ですので、是非覚えて活用して下さい。
そもそもDictionaryとは
Listに検索キーを付加してものとして考えると分かりやすいです。
Dictionary(辞書)は、検索するためのキーと、それに対応する値を1つのペアとして保持するクラスです。
キーをKey、値をValueと呼ぶのですが、2つまとめてKeyValuePare と呼んだりもします。
Key、Value のどちらも任意の型やクラスを指定できます。
例えば、文字列で実数を検索したい場合、 Dictionary<string,double > と宣言しますし、例えば自分で作った Person というクラスがあったとすると、Dictionary<Person,string>の様に宣言します。
値の登録
辞書にキーと値を登録する場合は、Addメソッドを使います。
1 2 3 4 5 |
//Dictionaryの宣言 Dictionary<string,int> dic = new Dictionary<string,int>(); //"体重" というキーと、それに対応する値をDictionaryに登録 dic.Add("体重",68); |
もう一つの方法として、添え字を使うことも可能です。この場合、新しい添え字を指定すれば、添え字をキーにして、代入した値が辞書に新規追加されますし、既にキーが存在していれば値が上書きされます。
1 |
dic["体重"] = 68; |
値の取り出し
キーを添え字に指定することで瞬時に対応する値が取得できます。
1 2 |
//"体重" というキーでDictionaryから値を取り出す int val = dic["体重"]; |
もし間違ったキーを添え字にしたらどうなるでしょう?
答えは例外エラーが発生してしまいます。
ですから、例えば画面から入力されたキーを使って検索するような場合は、あらかじめ ContainsKeyメソッドを使って、Dictionaryにキーがあるかを確認する必要があります。
1 2 3 4 |
if(dic.ContainsKey("体重") == true) { double val = dic["体重"] } |
値の削除
登録した値を削除したい場合は、Removeメソッドを使います。
ただし、これもキーが存在しないとエラーになりますので、値の取り出しと同じく、場合によってContainsKeyでの存在確認が必要です。
1 2 |
//体重というキーを値ごと削除する dic.Remove("体重"); |
全ての値を連続して取り出したい時
Dictionaryに登録されている全ての値を連続して取り出したい場合はfor ループを使います。
1つは、Keysプロパティを使ってキーを取り出し、そのキーを添え字にして値を取り出す方法です。
1 2 3 4 5 |
//辞書に登録されているKeyを取り出して、添え字に使う書き方 foreach (string key in dic.Keys) { Console.WriteLine( dic[key]); } |
もう1つは、KeyValuePare を使ってキーと値をまとめて取り出す方法です。
1 2 3 4 5 6 7 8 9 10 11 |
//KeyValuePareを使った書き方 foreach (KeyValuePair<string,int> kvp in dic) { Console.WriteLine( kvp.Value); } //KeyValuePareを var で省略した書き方 foreach (var kvp in dic) { Console.WriteLine( kvp.Value); } |
辞書は登録順が保持されない
Dictionaryを使う上で、時には取り出す順番が重要になることがあります。
例えば、伝票番号と金額をDictonaryに保持しつつ、登録した順番に取り出したいとか、伝票番号でソートして取り出したいとかです。
悲しいことに、DictionaryはAddメソッドで登録した瞬間に、検索速度を優先した並び順で保持されるため、forループで取り出したときの順番は保証されません。
実際には、 foreachのループ内でキーと値を取り出す場合、登録順で取り出せることも多々ありますが、C#の仕様上 Dictionary は順序が保証されていないため、登録順で取り出すことが必須の処理ではDictionaryを使うべきではありません。
そこで、登録するたびに自動で並び替え(ソート)を行ってくれる SortedDictionaryと、登録した順番を保持してくれる OrderedDictionary が用意されています。
自動でキーをソートしてくれるSortedDictionary
SortedDictonary は、自動でKeyを昇順(1,2,3,4,5・・・)で並び替えてくれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//Dictonaryの宣言と値の登録 SortedDictionary<int, int> dic = new SortedDictionary<int, int>(); dic.Add(5, 500); dic.Add(4, 400); dic.Add(2, 200); dic.Add(1, 100); dic.Add(3, 300); dic.Add(6, 600); //キーと値を取り出すループ foreach (KeyValuePair<int, int> kvp in dic) { Console.WriteLine("key={0},value={1}",kvp.Key,kvp.Value); } |
答えは次の様になります。
1 2 3 4 5 6 7 |
key=1,value=100 key=2,value=200 key=3,value=300 key=4,value=400 key=5,value=500 key=6,value=600 |
登録順を保持してくれるOrderedDictionary
OrderedDictionaryは、Keyの登録順、つまりAddメソッドを実行した順番を保持してくれるものです。
ただ、残念ながら非ジェネリック版(宣言時にキーや値の型が指定できず、すべてobject型になる)なので、KeyValuePare での取り出しは出来ません。
ちょっとややこしいですが、次の様な書き方でキーと値を順番に取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//OrderedDictionaryを使う時は、次の2行を冒頭の参照に追加 using System.Collections; using System.Collections.Specialized; //OrderedDictionaryの初期化と値の登録 OrderedDictionary dic = new OrderedDictionary(); dic.Add(5, 500); dic.Add(4, 400); dic.Add(2, 200); dic.Add(1, 100); dic.Add(3, 300); dic.Add(6, 600); IDictionaryEnumerator enumerator = dic.GetEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine("key={0},value={1}", enumerator.Key, enumerator.Value); } |
答えは次の様になります。
1 2 3 4 5 6 7 |
key=5,value=500 key=4,value=400 key=2,value=200 key=1,value=100 key=3,value=300 key=6,value=600 |
Linqを使うと通常のDictionaryでも並べ替えは可能
Dictionaryで並べ替えをしたいのであれば、Linqを使う事でも対応が可能です。
OrderBy(i=>i.Key) でキーによる並べ替え、OrderBy(i=>i.Value)で値による並べ替えができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Dictionary<int, int> dic = new Dictionary<int, int>(); dic.Add(5, 500); dic.Add(4, 400); dic.Add(2, 200); dic.Add(1, 100); dic.Add(3, 300); dic.Add(6, 600); foreach (KeyValuePair<int, int> kvp in dic.OrderBy(i=>i.Key)) { Console.WriteLine("key={0},value={1}", kvp.Key, kvp.Value); } |
OrderByは昇順になりますが、降順にしたい場合、OrderByDescendingが利用可能です。
1 2 3 4 |
foreach (KeyValuePair<int, int> kvp in dic.OrderByDescending(i=>i.Key)) { Console.WriteLine("key={0},value={1}", kvp.Key, kvp.Value); } |
まとめ
いかがでしたでしょうか。
通常のDictionaryは、検索しやすい並びで保持されているため、並び順が保証されていません。
その代わり、並び順を保証してくれる SortedDictionaryとOrderedDictionaryが用意されています。
もし、並び順が必要になったら、この記事を参考にして頂ければ幸いです。