配列やListと並んで使われるDictionaryですが、実は用途に応じていくつかの種類が存在します。
今回は、C#で使えるDictionaryの種類と使い道について解説していきます。
用途に応じて使い分けることが大切ですので、是非覚えて活用して下さい。
そもそもDictionaryとは
Listに検索キーを付加してものとして考えると分かりやすいです。
Dictionary(辞書)は、検索するためのキーと、それに対応する値を1つのペアとして保持するクラスです。
キーをKey、値をValueと呼ぶのですが、2つまとめてKeyValuePare と呼んだりもします。
Key、Value のどちらも任意の型やクラスを指定できます。
例えば、文字列で実数を検索したい場合、 Dictionary<string,double > と宣言しますし、例えば自分で作った Person というクラスがあったとすると、Dictionary<Person,string>の様に宣言します。
値の登録
辞書にキーと値を登録する場合は、Addメソッドを使います。
//Dictionaryの宣言
Dictionary<string,int> dic = new Dictionary<string,int>();
//"体重" というキーと、それに対応する値をDictionaryに登録
dic.Add("体重",68);
もう一つの方法として、添え字を使うことも可能です。この場合、新しい添え字を指定すれば、添え字をキーにして、代入した値が辞書に新規追加されますし、既にキーが存在していれば値が上書きされます。
dic["体重"] = 68;
値の取り出し
キーを添え字に指定することで瞬時に対応する値が取得できます。
//"体重" というキーでDictionaryから値を取り出す
int val = dic["体重"];
もし間違ったキーを添え字にしたらどうなるでしょう?
答えは例外エラーが発生してしまいます。
ですから、例えば画面から入力されたキーを使って検索するような場合は、あらかじめ ContainsKeyメソッドを使って、Dictionaryにキーがあるかを確認する必要があります。
if(dic.ContainsKey("体重") == true)
{
double val = dic["体重"]
}
値の削除
登録した値を削除したい場合は、Removeメソッドを使います。
ただし、これもキーが存在しないとエラーになりますので、値の取り出しと同じく、場合によってContainsKeyでの存在確認が必要です。
//体重というキーを値ごと削除する
dic.Remove("体重");
全ての値を連続して取り出したい時
Dictionaryに登録されている全ての値を連続して取り出したい場合はfor ループを使います。
1つは、Keysプロパティを使ってキーを取り出し、そのキーを添え字にして値を取り出す方法です。
//辞書に登録されているKeyを取り出して、添え字に使う書き方
foreach (string key in dic.Keys)
{
Console.WriteLine( dic[key]);
}
もう1つは、KeyValuePare を使ってキーと値をまとめて取り出す方法です。
//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・・・)で並び替えてくれます。
//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);
}
答えは次の様になります。
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 での取り出しは出来ません。
ちょっとややこしいですが、次の様な書き方でキーと値を順番に取得できます。
//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);
}
答えは次の様になります。
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)で値による並べ替えができます。
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が利用可能です。
foreach (KeyValuePair<int, int> kvp in dic.OrderByDescending(i=>i.Key))
{
Console.WriteLine("key={0},value={1}", kvp.Key, kvp.Value);
}
まとめ
いかがでしたでしょうか。
通常のDictionaryは、検索しやすい並びで保持されているため、並び順が保証されていません。
その代わり、並び順を保証してくれる SortedDictionaryとOrderedDictionaryが用意されています。
もし、並び順が必要になったら、この記事を参考にして頂ければ幸いです。