こんにちは!
なかむぅです。
DictionaryクラスのメソッドでKeyが存在するかチェックできる
- ContainsKey(TKey key)
- TryGetValue(TKey key, out TValue value)
といった2つのメソッドがでてきて
となったので違いを調べてみました。
という方は以下の記事を参考にしてください。
ContainsKeyとTryGetValueの書き方
書き方は第二引数があるかないか。
if (dictionary.ContainsKey("hoge"))
{
Console.WriteLine($"「hoge」のKeyには「{dictionary["hoge"]}」が入っています");
}
if (dictionary.TryGetValue("hoge", out string value))
{
Console.WriteLine($"「hoge」のKeyには「{value}」が入っています");
}
TryGetValue
は一度Keyを渡せばout
の変数に値が入るので、値の取得が楽ちんです。
ContainsKey
はKeyを指定して値を取得しないといけないので、その分のコストがかかります。
ついでに中身も見てみたのですが、どちらもFindEntry
を呼び出してチェックしていました。
ContainsKey
は演算子で比較してそのまま返していますが、TryGetValue
はout
で渡ってきた引数に値を入れてから返しています。
public bool TryGetValue(TKey key, out TValue value)
{
int i = FindEntry(key);
if (i >= 0)
{
value = entries[i].value;
return true;
}
value = default(TValue);
return false;
}
public bool ContainsKey(TKey key)
{
return FindEntry(key) >= 0;
}
実際のFindEntry
の中身はこちら。
private int FindEntry(TKey key)
{
if (key == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null)
{
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next)
{
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
書き方についてはTryGetValue
のほうがスッキリ書けて良いかなと思います。
存在しないKeyを取得しようとしたとき
存在しないKeyを取得しようとしたときは以下のような結果になります。
- ContainsKeyは例外が発生
- TryGetValueはvalueが型のdefault値が返ってくる
var dictionary = new Dictionary<string, string>();
if (!dictionary.ContainsKey("hoge"))
{
try
{
Console.WriteLine($"ContainsKey:{dictionary["hoge"]}");
}
catch
{
// 例外が発生する
Console.WriteLine($"ContainsKeyは例外");
}
}
if (!dictionary.TryGetValue("hoge", out string value))
{
if (value is null)
{
// string型のデフォルト値nullが返ってきている
Console.WriteLine($"TryGetValueのvalueはnull");
}
}
ContainsKeyは例外
TryGetValueの valueは null
中身を見てみると、TryGetValue
は例外が発生しないようにout
で渡ってきた引数にdefault
を入れて返しているからでした。
public bool TryGetValue(TKey key, out TValue value)
{
int i = FindEntry(key);
if (i >= 0)
{
value = entries[i].value;
return true;
}
value = default(TValue);
return false;
}
TryGetValue
はdefault
値のまま行ってしまいますが、ContainsKey
は例外が発生するので、どこに問題があるかがわかりやすくなると思います。
ContainsKeyとTryGetValueの速度
ContainsKey
とTryGetValue
でどちらが速いか測定してみました。
Count
プロパティ分のDictionary
を作って、Count
プロパティ分のList
にセットしていく処理にしています。
回数は1000回と10000回で行いました。
private int Count { get; } = 1000; // セットするKeyの数
public void CheckContainsKeyVsTryGetValue()
{
// ContainsKeyとTryGetValueの測定を入れ替えると結果が変わってしまう
CheckContainsKey(GetDictionary()); //①
CheckTryGetValue(GetDictionary()); //②
}
private Dictionary<int, int> GetDictionary()
{
var dictionary = new Dictionary<int, int>();
for (int i = 0; i < Count; i++)
{
dictionary.Add(i, i);
}
return dictionary;
}
private void CheckContainsKey(Dictionary<int, int> dictionary)
{
var list = new List<int>(Count);
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < Count; i++)
{
if (dictionary.ContainsKey(i)) list.Add(dictionary[i]);
}
sw.Stop();
Console.WriteLine($"ContainsKey :{sw.Elapsed} ms");
}
private void CheckTryGetValue(Dictionary<int, int> dictionary)
{
var list = new List<int>(Count);
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < Count; i++)
{
if (dictionary.TryGetValue(i, out int value)) list.Add(value);
}
sw.Stop();
Console.WriteLine($"TryGetValue :{sw.Elapsed} ms");
}
ちなみに、上記のソースコードだとContainsKey
とTryGetValue
の測定する順番を入れ替えたときに結果が変わってしまったので、それぞれコメントアウトして1つずつ測定してます。
同時に行うと、後から測定した方が速くなってしまうみたいです。
ContainsKey : 00:00:00.0000448 ms
TryGetValue : 00:00:00.0000212 ms
TryGetValue : 00:00:00.0000359 ms
ContainsKey : 00:00:00.0000288 ms
なので、以下の手順で測定しました。
- ②のCheckTryGetValueをコメントアウトして①のCheckContainsKeyを実行
- ①のCheckContainsKeyをコメントアウトして②のCheckTryGetValueを実行
結果、TryGetValue
のほうが速かったです。
ContainsKey : 00:00:00.0000450 ms
TryGetValue : 00:00:00.0000354 ms
ContainsKey : 00:00:00.0002018 ms
TryGetValue : 00:00:00.0001414 ms
速度はTryGetValue
のほうが速いですね。
私が働く会社では、一緒に働いてくれるエンジニアを募集しています♪
- 長く働いていても年収が全然上がらない…一気に年収を上げたい
- 残業をしたくてしているわけではないので、残業代はしっかりと出して欲しい
- やりたいことに挑戦させてもらえない…自分がしてみたいことに挑戦したい
- 通勤時間が長すぎるから、できるだけ短くしたい
- もっと勉強したいから、書籍代や資格の受験料など負担してくれるところに入りたい
- 現場に駆り出されてからは放置プレイ…相談できる人も先輩も居ないので、自分の状況をちゃんと理解してくれるところで働きたい
上記内容に1つでも当てはまる場合は、ぜひお声がけください。
私のサイトから応募していただいた方にはお好きなギフト券5000円分プレゼントさせていただきます(条件あり)。
まとめ:結論
ContainsKey | TryGetValue | |
---|---|---|
記述量 | × | ○ |
不具合の検知 | ○ | × |
速度 | × | ○ |
結論、TryGetValue
を使用するのが良さそうですね。
否定演算子を使ってdefault
値が返ってきたときの処理をしっかりと書けば、Keyがセットされていないときの対処もできると思います。
if (dictionary.TryGetValue(i, out int value))
{
// default値が返ってきた時の処理
}
他に比較する要素が見当たらないので、もし他にもあればコメントください。
以上、 ContainsKey
とTryGetValue
についてでした!
と思った方はコメントいただけると嬉しいです。
最後までお読みいただきありがとうございました!