139*_*ser 6 .net c# dictionary
我已经尝试了一些测试,如果有所有的点击或错过也没关系.TryGetValue总是更快.什么时候应该使用ContainsKey?
myb*_*ame 19
这取决于你使用的方法是什么.如果您打开参考源,您将看到.
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);
}
Run Code Online (Sandbox Code Playgroud)
就像你看到TryGetValue的那样与ContainsKey+一个数组查找相同.
如果您的逻辑只是检查密钥是否存在于该密钥中,Dictionary并且没有与该密钥相关的其他内容(获取密钥的值),则应使用ContainsKey.
如果要获取特定键的值,TryGetValue则快于
if(dic.ContainsKey(keyValue))
{
dicValue = dic[keyValue]; // here you do more work!
}
Run Code Online (Sandbox Code Playgroud)
逻辑关于 Dictionary[key]
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);
}
}
Run Code Online (Sandbox Code Playgroud)
所以你在FindEntry方法+数组查找中会去2次,如果你使用键ContainsKey和之后使用该值dic[key].你有FindEntry一个额外的时间调用的开销.
从概念上讲,这两种方法非常不同.ContainsKey只是检查给定的键是否在字典中.TryGetValue将尝试返回给定键的值,前提是它在字典中.两者都可以很快,具体取决于你想做什么.
请考虑以下方法,该方法从字典返回值或返回string.Empty.
Dictionary<int,string> Dict = new Dictionary<int,string>();
string GetValue1(int key)
{
string outValue;
if (!Dict.TryGetValue(key, out outValue))
outValue = string.Empty;
return outValue;
}
Run Code Online (Sandbox Code Playgroud)
相比
string GetValue2(int key)
{
return (Dict.ContainsKey(key))
? Dict[key]
: string.Empty;
}
Run Code Online (Sandbox Code Playgroud)
两者都相对较快,并且在大多数情况下两者之间的性能可以忽略不计(参见下面的单元测试).如果我们想要挑剔,使用TryGetValue需要使用out参数,这比常规参数更有开销.如果未找到该类型,则此变量将设置为默认值,对于字符串,该变量为null.在上面的示例中,不使用此null值,但无论如何我们都会产生开销.最后,GetValue1需要使用局部变量outValue,而GetValue2则不需要.
BACON指出GetValue2会在找到值的情况下使用2次查找,这是相对费用的.这是正确的,并且还意味着在未找到密钥的情况下,GetValue2将执行得更快.因此,如果程序期望大多数查找都未命中,请使用GetValue2.否则使用GetValue1.
如果处理ConcurrentDictionary,请使用TryGetValue,因为对它的操作应该是原子的,即一旦在多线程环境中检查值,当您尝试访问该值时,该检查可能不正确.
下面包括2个单元测试,使用具有不同Key类型的字典(int vs string)测试两个方法之间的性能.该基准仅显示两种方法之间的差距如何随着其上下文的变化而关闭/扩大.
[TestMethod]
public void TestString()
{
int counter = 10000000;
for (var x = 0; x < counter; x++)
DictString.Add(x.ToString(), "hello");
TimedLog("10,000,000 hits TryGet", () =>
{
for (var x = 0; x < counter; x++)
Assert.IsFalse(string.IsNullOrEmpty(GetValue1String(x.ToString())));
}, Console.WriteLine);
TimedLog("10,000,000 hits ContainsKey", () =>
{
for (var x = 0; x < counter; x++)
Assert.IsFalse(string.IsNullOrEmpty(GetValue2String(x.ToString())));
}, Console.WriteLine);
TimedLog("10,000,000 misses TryGet", () =>
{
for (var x = counter; x < counter*2; x++)
Assert.IsTrue(string.IsNullOrEmpty(GetValue1String(x.ToString())));
}, Console.WriteLine);
TimedLog("10,000,000 misses ContainsKey", () =>
{
for (var x = counter; x < counter*2; x++)
Assert.IsTrue(string.IsNullOrEmpty(GetValue2String(x.ToString())));
}, Console.WriteLine);
}
[TestMethod]
public void TestInt()
{
int counter = 10000000;
for (var x = 0; x < counter; x++)
DictInt.Add(x, "hello");
TimedLog("10,000,000 hits TryGet", () =>
{
for (var x = 0; x < counter; x++)
Assert.IsFalse(string.IsNullOrEmpty(GetValue1Int(x)));
}, Console.WriteLine);
TimedLog("10,000,000 hits ContainsKey", () =>
{
for (var x = 0; x < counter; x++)
Assert.IsFalse(string.IsNullOrEmpty(GetValue2Int(x)));
}, Console.WriteLine);
TimedLog("10,000,000 misses TryGet", () =>
{
for (var x = counter; x < counter * 2; x++)
Assert.IsTrue(string.IsNullOrEmpty(GetValue1Int(x)));
}, Console.WriteLine);
TimedLog("10,000,000 misses ContainsKey", () =>
{
for (var x = counter; x < counter * 2; x++)
Assert.IsTrue(string.IsNullOrEmpty(GetValue2Int(x)));
}, Console.WriteLine);
}
public static void TimedLog(string message, Action toPerform, Action<string> logger)
{
var start = DateTime.Now;
if (logger != null)
logger.Invoke(string.Format("{0} Started at {1:G}", message, start));
toPerform.Invoke();
var end = DateTime.Now;
var span = end - start;
if (logger != null)
logger.Invoke(string.Format("{0} Ended at {1} lasting {2:G}", message, end, span));
}
Run Code Online (Sandbox Code Playgroud)
TestInt的结果
10,000,000 hits TryGet Started at ...
10,000,000 hits TryGet Ended at ... lasting 0:00:00:00.3734136
10,000,000 hits ContainsKey Started at ...
10,000,000 hits ContainsKey Ended at ... lasting 0:00:00:00.4657632
10,000,000 misses TryGet Started at ...
10,000,000 misses TryGet Ended at ... lasting 0:00:00:00.2921058
10,000,000 misses ContainsKey Started at ...
10,000,000 misses ContainsKey Ended at ... lasting 0:00:00:00.2579766
Run Code Online (Sandbox Code Playgroud)
对于命中,ContainsKey的TryGetValue减慢约25%
对于未命中,TryGetValue比ContainsKey慢约13%
TestString的结果
10,000,000 hits TryGet Started at ...
10,000,000 hits TryGet Ended at ... lasting 0:00:00:03.2232018
10,000,000 hits ContainsKey Started at ...
10,000,000 hits ContainsKey Ended at ... lasting 0:00:00:03.6417864
10,000,000 misses TryGet Started at ...
10,000,000 misses TryGet Ended at ... lasting 0:00:00:03.6508206
10,000,000 misses ContainsKey Started at ...
10,000,000 misses ContainsKey Ended at ... lasting 0:00:00:03.4912164
Run Code Online (Sandbox Code Playgroud)
对于命中,ContainsKey的TryGetValue减慢约13%
对于未命中,TryGetValue比ContainsKey慢约4.6%
| 归档时间: |
|
| 查看次数: |
5456 次 |
| 最近记录: |