最近在进行在做一个链路选择的需求,涉及到字典存储。发现C#的Dictionary提供了ContainsKey和TryGetValue两个方法,都可以来判断字典中是否存在对应的Key值。那么这两个方法有什么区别呢?我们在编写代码的过程中如何选取呢?
我先创建了一个Dictionary<string, int>的字典,然后从0递增到一千万分别给这个字典添加了数据。
static Dictionary<string, int> dic = new Dictionary<string, int>();
const int TOTALNUM = 10000000;
for (int i = 0; i < TOTALNUM; i++)
{
dic.Add(i.ToString(), i);
}
我们借助StopWatch,对ContainsKey和TryGetValue分别进行一千万次的查找,比较两者所花费的时间。
Stopwatch stopwatch1 = new Stopwatch(); // TryGetValue
Stopwatch stopwatch2 = new Stopwatch(); // COntainsKey
测试环境:
- IDE工具:VS2019
- CPU:i7-8700
-
当取出的Value在执行过程中有被使用时:
stopwatch1.Start(); long a = 0; for (int i = 0; i < TOTALNUM; i++) { string str = i.ToString(); if (dic.TryGetValue(str, out var value)) { // Value被使用 a = a + value; } } stopwatch1.Stop(); stopwatch2.Start(); long b = 0; for (int i = 0; i < TOTALNUM; i++) { string str = i.ToString(); if (dic.ContainsKey(str)) { // Value被使用 b = b + dic[str]; } } stopwatch2.Stop();
ContainsKey所花费的时间较多
-
当没有对字典所对应Key的Value进行操作时:
... if (dic.TryGetValue(str, out var value)) { a = a + 1; } ... ... if (dic.ContainsKey(str)) { b = b + 1; } ...
TryGetValue花费的时间更多
-
查看源码:
public bool TryGetValue(TKey key, out TValue value) { int index = this.FindEntry(key); if (index >= 0) { value = this.entries[index].value; return true; } value = default(TValue); return false; } public bool ContainsKey(TKey key) { return (this.FindEntry(key) >= 0); } public TValue this[TKey key] { get { int i = FindEntry(key); if (i >= 0) return entries[i].value; ThrowHelper.ThrowKeyNotFoundException(); return default(TValue); } set { Insert(key, value, false); } }
查看源码我们发现TryGetValue是在ContainsKey的基础上直接从存储中取出Value。有一个很关键的函数FindEntry我们在三个函数都有看到,说明我们如果在使用完ContainsKey在用Dic[Key]去取值的时候,会多一次开销。
总结
- 在数据量不大的情况下,两者并没有太大的差别。在数据量大的情况下,如果对Value并没有操作推荐用ContainsKey,反之则使用TryGetValue。当Key的命中率远低于50%,且操作Value时,也可以考虑使用ContainsKey。