从MSDN的词条词典。TryGetValue方法:
This method combines the functionality of the ContainsKey method and
the Item property.
If the key is not found, then the value parameter gets the appropriate
default value for the value type TValue; for example, 0 (zero) for
integer types, false for Boolean types, and null for reference types.
Use the TryGetValue method if your code frequently attempts to access
keys that are not in the dictionary. Using this method is more
efficient than catching the KeyNotFoundException thrown by the Item
property.
This method approaches an O(1) operation.
从描述中,不清楚它是否比调用ContainsKey然后进行查找更高效,还是更方便。TryGetValue的实现只是调用ContainsKey,然后Item,还是实际上比做单个查找更有效?
换句话说,哪个更有效(即哪个查找更少):
Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
ival = dict[ikey];
}
else
{
ival = default(int);
}
or
Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);
注意:我不是在寻找一个基准!
一个快速的基准测试显示TryGetValue略有优势:
static void Main() {
var d = new Dictionary<string, string> {{"a", "b"}};
var start = DateTime.Now;
for (int i = 0; i != 10000000; i++) {
string x;
if (!d.TryGetValue("a", out x)) throw new ApplicationException("Oops");
if (d.TryGetValue("b", out x)) throw new ApplicationException("Oops");
}
Console.WriteLine(DateTime.Now-start);
start = DateTime.Now;
for (int i = 0; i != 10000000; i++) {
string x;
if (d.ContainsKey("a")) {
x = d["a"];
} else {
x = default(string);
}
if (d.ContainsKey("b")) {
x = d["b"];
} else {
x = default(string);
}
}
}
这个生产
00:00:00.7600000
00:00:01.0610000
使ContainsKey + Item访问速度慢了40%,假设命中和错过的混合均匀。
此外,当我将程序更改为总是错过(即总是查找“b”)时,两个版本变得同样快:
00:00:00.2850000
00:00:00.2720000
然而,当我让它“all hits”时,TryGetValue仍然是一个明显的赢家:
00:00:00.4930000
00:00:00.8110000
如果你试图从字典中获取值,TryGetValue(key, out value)是最好的选择,但如果你正在检查是否存在键,对于一个新的插入,而不覆盖旧键,并且仅在该范围内,ContainsKey(key)是最好的选择,基准测试可以确认这一点:
using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;
namespace benchmark
{
class Program
{
public static Random m_Rand = new Random();
public static Dictionary<int, int> testdict = new Dictionary<int, int>();
public static Hashtable testhash = new Hashtable();
public static void Main(string[] args)
{
Console.WriteLine("Adding elements into hashtable...");
Stopwatch watch = Stopwatch.StartNew();
for(int i=0; i<1000000; i++)
testhash[i]=m_Rand.Next();
watch.Stop();
Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
Thread.Sleep(4000);
Console.WriteLine("Adding elements into dictionary...");
watch = Stopwatch.StartNew();
for(int i=0; i<1000000; i++)
testdict[i]=m_Rand.Next();
watch.Stop();
Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
Thread.Sleep(4000);
Console.WriteLine("Finding the first free number for insertion");
Console.WriteLine("First method: ContainsKey");
watch = Stopwatch.StartNew();
int intero=0;
while (testdict.ContainsKey(intero))
{
intero++;
}
testdict.Add(intero, m_Rand.Next());
watch.Stop();
Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
Thread.Sleep(4000);
Console.WriteLine("Second method: TryGetValue");
watch = Stopwatch.StartNew();
intero=0;
int result=0;
while(testdict.TryGetValue(intero, out result))
{
intero++;
}
testdict.Add(intero, m_Rand.Next());
watch.Stop();
Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
Thread.Sleep(4000);
Console.WriteLine("Test hashtable");
watch = Stopwatch.StartNew();
intero=0;
while(testhash.Contains(intero))
{
intero++;
}
testhash.Add(intero, m_Rand.Next());
watch.Stop();
Console.WriteLine("Done in {0:F4} -- added value {1} into hashtable -- pause....", watch.Elapsed.TotalSeconds, intero);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
这是一个真实的例子,我有一个服务,对于每个创建的“Item”,它关联一个累进的数字,这个数字,每次你创建一个新项目,必须找到空闲的,如果你删除一个Item,空闲的数字变成空闲的,当然这不是优化的,因为我有一个静态的变量,缓存当前的数字,但如果你结束所有的数字,你可以从0到UInt32重新开始。MaxValue
测试执行:
在哈希表中添加元素…
完成于0,5908 -暂停....
向字典中添加元素…
完成于0,2679 -暂停....
找到第一个可插入的空闲数
第一种方法:ContainsKey
在0,0561中完成—在字典中添加值1000000—暂停....
第二个方法:TryGetValue
在0,0643完成—在字典中添加值1000001—暂停....
测试散列表
在0,3015完成——在哈希表中添加值1000000——暂停....
按任意键继续。
如果你们中的一些人可能会问ContainsKeys是否有优势,我甚至尝试过用Contains key反转TryGetValue,结果是一样的。
所以,对我来说,最后考虑一下,这一切都取决于程序的行为方式。
制作一个快速测试程序,使用字典中包含100万个项的TryGetValue显然有很大的改进。
结果:
ContainsKey + Item为1000000次:45ms
1000000次的TryGetValue: 26ms
下面是测试应用程序:
static void Main(string[] args)
{
const int size = 1000000;
var dict = new Dictionary<int, string>();
for (int i = 0; i < size; i++)
{
dict.Add(i, i.ToString());
}
var sw = new Stopwatch();
string result;
sw.Start();
for (int i = 0; i < size; i++)
{
if (dict.ContainsKey(i))
result = dict[i];
}
sw.Stop();
Console.WriteLine("ContainsKey + Item for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < size; i++)
{
dict.TryGetValue(i, out result);
}
sw.Stop();
Console.WriteLine("TryGetValue for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);
}