最近在進行在做一個鏈路選擇的需求,涉及到字典存儲。發現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。