C# 什么更有效:Dictionary TryGetValue或ContainsKey+;项目?
从MSDN的条目: 此方法结合了ContainsKey方法和 项属性 如果找不到键,则value参数将获得适当的 值类型TValue的默认值;例如,0(零)表示 整数类型,布尔类型为false,引用类型为null 如果代码经常尝试访问,请使用TryGetValue方法 字典中没有的键。使用这种方法更有效 比捕获项引发的KeyNotFoundException更有效 财产 此方法接近O(1)操作 从描述来看,它是否比调用ContainsKey然后执行查找更高效或更方便还不清楚。C# 什么更有效:Dictionary TryGetValue或ContainsKey+;项目?,c#,performance,dictionary,C#,Performance,Dictionary,从MSDN的条目: 此方法结合了ContainsKey方法和 项属性 如果找不到键,则value参数将获得适当的 值类型TValue的默认值;例如,0(零)表示 整数类型,布尔类型为false,引用类型为null 如果代码经常尝试访问,请使用TryGetValue方法 字典中没有的键。使用这种方法更有效 比捕获项引发的KeyNotFoundException更有效 财产 此方法接近O(1)操作 从描述来看,它是否比调用ContainsKey然后执行查找更高效或更方便还不清楚。TryGetValu
TryGetValue
的实现是只调用ContainsKey,然后调用Item,还是实际上比执行单个查找更有效
换句话说,什么更有效(即哪一个执行的查找更少):
字典dict;
//...//
国际竞争力;
if(dict.ContainsKey(ikey))
{
ival=dict[ikey];
}
其他的
{
ival=默认值(int);
}
或
字典dict;
//...//
国际竞争力;
dict.TryGetValue(ikey,out-ival);
注意:我不是在寻找基准 为什么不测试一下呢
但是我很确定TryGetValue
更快,因为它只进行一次查找。当然,这并不能保证,即不同的实现可能具有不同的性能特征
我实现字典的方法是创建一个内部的
Find
函数来查找项目的槽,然后在其上构建其余的。TryGetValue
会更快
ContainsKey
使用与TryGetValue
相同的检查,该检查在内部表示实际输入位置。项
属性实际上与TryGetValue
具有几乎相同的代码功能,只是它将抛出异常而不是返回false
使用
ContainsKey
后跟项
基本上复制了查找功能,这是本例中的大部分计算 快速基准测试表明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);
}
}
}
假设命中率和未命中率相等,使ContainsKey+项的访问速度降低约40%
此外,当我将程序更改为“始终未命中”(即始终查找“b”
)时,两个版本的速度相同:
00:00:00.2850000
00:00:00.2720000
然而,当我将其设置为“所有命中率”时,TryGetValue
仍然是一个明显的赢家:
00:00:00.4930000
00:00:00.8110000
做一个快速测试程序,使用TryGetValue在字典中包含100万项肯定会有改进
结果:
Finished evaluation .... Time distribution:
Size: 000010: TryGetValue: 53,24%, ContainsKey: 1,74%, try-catch: 45,01% - Total: 2.006,00
Size: 000020: TryGetValue: 37,66%, ContainsKey: 0,53%, try-catch: 61,81% - Total: 2.443,00
Size: 000040: TryGetValue: 22,02%, ContainsKey: 0,73%, try-catch: 77,25% - Total: 7.147,00
Size: 000080: TryGetValue: 31,46%, ContainsKey: 0,42%, try-catch: 68,12% - Total: 17.793,00
Size: 000160: TryGetValue: 33,66%, ContainsKey: 0,37%, try-catch: 65,97% - Total: 36.840,00
Size: 000320: TryGetValue: 34,53%, ContainsKey: 0,39%, try-catch: 65,09% - Total: 71.059,00
Size: 000640: TryGetValue: 32,91%, ContainsKey: 0,32%, try-catch: 66,77% - Total: 141.789,00
Size: 001280: TryGetValue: 39,02%, ContainsKey: 0,35%, try-catch: 60,64% - Total: 244.657,00
Size: 002560: TryGetValue: 35,48%, ContainsKey: 0,19%, try-catch: 64,33% - Total: 420.121,00
Size: 005120: TryGetValue: 43,41%, ContainsKey: 0,24%, try-catch: 56,34% - Total: 625.969,00
Size: 010240: TryGetValue: 29,64%, ContainsKey: 0,61%, try-catch: 69,75% - Total: 1.197.242,00
Size: 020480: TryGetValue: 35,14%, ContainsKey: 0,53%, try-catch: 64,33% - Total: 2.405.821,00
Size: 040960: TryGetValue: 37,28%, ContainsKey: 0,24%, try-catch: 62,48% - Total: 4.200.839,00
Size: 081920: TryGetValue: 29,68%, ContainsKey: 0,54%, try-catch: 69,77% - Total: 8.980.230,00
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int ENTRIES = 10000, MAXVAL = 15000, TRIALS = 100000, MULTIPLIER = 2;
Dictionary<int, int> values = new Dictionary<int, int>();
Random r = new Random();
int[] lookups = new int[TRIALS];
int val;
List<Tuple<long, long, long>> durations = new List<Tuple<long, long, long>>(8);
for (int i = 0;i < ENTRIES;++i) try
{
values.Add(r.Next(MAXVAL), r.Next());
}
catch { --i; }
for (int i = 0;i < TRIALS;++i) lookups[i] = r.Next(MAXVAL);
Stopwatch sw = new Stopwatch();
ConsoleColor bu = Console.ForegroundColor;
for (int size = 10;size <= TRIALS;size *= MULTIPLIER)
{
long a, b, c;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Loop size: {0}", size);
Console.ForegroundColor = bu;
// ---------------------------------------------------------------------
sw.Start();
for (int i = 0;i < size;++i) values.TryGetValue(lookups[i], out val);
sw.Stop();
Console.WriteLine("TryGetValue: {0}", a = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i) val = values.ContainsKey(lookups[i]) ? values[lookups[i]] : default(int);
sw.Stop();
Console.WriteLine("ContainsKey: {0}", b = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i)
try { val = values[lookups[i]]; }
catch { }
sw.Stop();
Console.WriteLine("try-catch: {0}", c = sw.ElapsedTicks);
// ---------------------------------------------------------------------
Console.WriteLine();
durations.Add(new Tuple<long, long, long>(a, b, c));
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Finished evaluation .... Time distribution:");
Console.ForegroundColor = bu;
val = 10;
foreach (Tuple<long, long, long> d in durations)
{
long sum = d.Item1 + d.Item2 + d.Item3;
Console.WriteLine("Size: {0:D6}:", val);
Console.WriteLine("TryGetValue: {0:P2}, ContainsKey: {1:P2}, try-catch: {2:P2} - Total: {3:N}", (decimal)d.Item1 / sum, (decimal)d.Item2 / sum, (decimal)d.Item3 / sum, sum);
val *= MULTIPLIER;
}
Console.WriteLine();
}
}
}
ContainsKey+项目点击1000000次:45毫秒
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);
}
static void Main(字符串[]args)
{
const int size=1000000;
var dict=新字典();
对于(int i=0;i
由于到目前为止,没有一个答案能够真正回答这个问题,因此,经过一些研究,我找到了一个可以接受的答案:
如果反编译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;
}
鉴于ContainsKey方法是:
public bool ContainsKey(TKey key)
{
return (this.FindEntry(key) >= 0);
}
因此TryGetValue只是ContainsKey加上数组查找(如果该项存在)
TryGetValue的速度似乎是ContainesKey+项组合的两倍。如果您试图从字典中取出值,TryGetValue(key,out value)是最好的选择,但是如果您正在检查是否存在该键,是否需要新的插入,而不覆盖旧键,并且仅在该范围内,ContainsKey(键)是最好的选择,benchmark可以证实这一点:
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);
}
}
}
使用系统;
使用系统线程;
使用系统诊断;
使用System.Collections.Generic;
使用系统集合;
名称空间基准
{
班级计划
{
公共静态随机m_Rand=新随机();
公共静态字典testdict=新字典();
公共静态哈希表testhash=新哈希表();
公共静态void Main(字符串[]args)
{
WriteLine(“将元素添加到哈希表…”);
秒表=Stopwatch.StartNew();
对于(int i=0;i在我的机器上,RAM加载,在释放模式(非调试)下运行时,ContainsKey
等于TryGetValue
/如果找到字典中的所有条目,请尝试catch
ContainsKey
在只有几个字典条目未找到时,其性能远远优于它们(在我下面的示例中,将MAXVAL
设置为大于条目的任何值,以丢失一些条目):
结果:
Finished evaluation .... Time distribution:
Size: 000010: TryGetValue: 53,24%, ContainsKey: 1,74%, try-catch: 45,01% - Total: 2.006,00
Size: 000020: TryGetValue: 37,66%, ContainsKey: 0,53%, try-catch: 61,81% - Total: 2.443,00
Size: 000040: TryGetValue: 22,02%, ContainsKey: 0,73%, try-catch: 77,25% - Total: 7.147,00
Size: 000080: TryGetValue: 31,46%, ContainsKey: 0,42%, try-catch: 68,12% - Total: 17.793,00
Size: 000160: TryGetValue: 33,66%, ContainsKey: 0,37%, try-catch: 65,97% - Total: 36.840,00
Size: 000320: TryGetValue: 34,53%, ContainsKey: 0,39%, try-catch: 65,09% - Total: 71.059,00
Size: 000640: TryGetValue: 32,91%, ContainsKey: 0,32%, try-catch: 66,77% - Total: 141.789,00
Size: 001280: TryGetValue: 39,02%, ContainsKey: 0,35%, try-catch: 60,64% - Total: 244.657,00
Size: 002560: TryGetValue: 35,48%, ContainsKey: 0,19%, try-catch: 64,33% - Total: 420.121,00
Size: 005120: TryGetValue: 43,41%, ContainsKey: 0,24%, try-catch: 56,34% - Total: 625.969,00
Size: 010240: TryGetValue: 29,64%, ContainsKey: 0,61%, try-catch: 69,75% - Total: 1.197.242,00
Size: 020480: TryGetValue: 35,14%, ContainsKey: 0,53%, try-catch: 64,33% - Total: 2.405.821,00
Size: 040960: TryGetValue: 37,28%, ContainsKey: 0,24%, try-catch: 62,48% - Total: 4.200.839,00
Size: 081920: TryGetValue: 29,68%, ContainsKey: 0,54%, try-catch: 69,77% - Total: 8.980.230,00
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int ENTRIES = 10000, MAXVAL = 15000, TRIALS = 100000, MULTIPLIER = 2;
Dictionary<int, int> values = new Dictionary<int, int>();
Random r = new Random();
int[] lookups = new int[TRIALS];
int val;
List<Tuple<long, long, long>> durations = new List<Tuple<long, long, long>>(8);
for (int i = 0;i < ENTRIES;++i) try
{
values.Add(r.Next(MAXVAL), r.Next());
}
catch { --i; }
for (int i = 0;i < TRIALS;++i) lookups[i] = r.Next(MAXVAL);
Stopwatch sw = new Stopwatch();
ConsoleColor bu = Console.ForegroundColor;
for (int size = 10;size <= TRIALS;size *= MULTIPLIER)
{
long a, b, c;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Loop size: {0}", size);
Console.ForegroundColor = bu;
// ---------------------------------------------------------------------
sw.Start();
for (int i = 0;i < size;++i) values.TryGetValue(lookups[i], out val);
sw.Stop();
Console.WriteLine("TryGetValue: {0}", a = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i) val = values.ContainsKey(lookups[i]) ? values[lookups[i]] : default(int);
sw.Stop();
Console.WriteLine("ContainsKey: {0}", b = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i)
try { val = values[lookups[i]]; }
catch { }
sw.Stop();
Console.WriteLine("try-catch: {0}", c = sw.ElapsedTicks);
// ---------------------------------------------------------------------
Console.WriteLine();
durations.Add(new Tuple<long, long, long>(a, b, c));
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Finished evaluation .... Time distribution:");
Console.ForegroundColor = bu;
val = 10;
foreach (Tuple<long, long, long> d in durations)
{
long sum = d.Item1 + d.Item2 + d.Item3;
Console.WriteLine("Size: {0:D6}:", val);
Console.WriteLine("TryGetValue: {0:P2}, ContainsKey: {1:P2}, try-catch: {2:P2} - Total: {3:N}", (decimal)d.Item1 / sum, (decimal)d.Item2 / sum, (decimal)d.Item3 / sum, sum);
val *= MULTIPLIER;
}
Console.WriteLine();
}
}
}
这是我的代码:
Finished evaluation .... Time distribution:
Size: 000010: TryGetValue: 53,24%, ContainsKey: 1,74%, try-catch: 45,01% - Total: 2.006,00
Size: 000020: TryGetValue: 37,66%, ContainsKey: 0,53%, try-catch: 61,81% - Total: 2.443,00
Size: 000040: TryGetValue: 22,02%, ContainsKey: 0,73%, try-catch: 77,25% - Total: 7.147,00
Size: 000080: TryGetValue: 31,46%, ContainsKey: 0,42%, try-catch: 68,12% - Total: 17.793,00
Size: 000160: TryGetValue: 33,66%, ContainsKey: 0,37%, try-catch: 65,97% - Total: 36.840,00
Size: 000320: TryGetValue: 34,53%, ContainsKey: 0,39%, try-catch: 65,09% - Total: 71.059,00
Size: 000640: TryGetValue: 32,91%, ContainsKey: 0,32%, try-catch: 66,77% - Total: 141.789,00
Size: 001280: TryGetValue: 39,02%, ContainsKey: 0,35%, try-catch: 60,64% - Total: 244.657,00
Size: 002560: TryGetValue: 35,48%, ContainsKey: 0,19%, try-catch: 64,33% - Total: 420.121,00
Size: 005120: TryGetValue: 43,41%, ContainsKey: 0,24%, try-catch: 56,34% - Total: 625.969,00
Size: 010240: TryGetValue: 29,64%, ContainsKey: 0,61%, try-catch: 69,75% - Total: 1.197.242,00
Size: 020480: TryGetValue: 35,14%, ContainsKey: 0,53%, try-catch: 64,33% - Total: 2.405.821,00
Size: 040960: TryGetValue: 37,28%, ContainsKey: 0,24%, try-catch: 62,48% - Total: 4.200.839,00
Size: 081920: TryGetValue: 29,68%, ContainsKey: 0,54%, try-catch: 69,77% - Total: 8.980.230,00
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int ENTRIES = 10000, MAXVAL = 15000, TRIALS = 100000, MULTIPLIER = 2;
Dictionary<int, int> values = new Dictionary<int, int>();
Random r = new Random();
int[] lookups = new int[TRIALS];
int val;
List<Tuple<long, long, long>> durations = new List<Tuple<long, long, long>>(8);
for (int i = 0;i < ENTRIES;++i) try
{
values.Add(r.Next(MAXVAL), r.Next());
}
catch { --i; }
for (int i = 0;i < TRIALS;++i) lookups[i] = r.Next(MAXVAL);
Stopwatch sw = new Stopwatch();
ConsoleColor bu = Console.ForegroundColor;
for (int size = 10;size <= TRIALS;size *= MULTIPLIER)
{
long a, b, c;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Loop size: {0}", size);
Console.ForegroundColor = bu;
// ---------------------------------------------------------------------
sw.Start();
for (int i = 0;i < size;++i) values.TryGetValue(lookups[i], out val);
sw.Stop();
Console.WriteLine("TryGetValue: {0}", a = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i) val = values.ContainsKey(lookups[i]) ? values[lookups[i]] : default(int);
sw.Stop();
Console.WriteLine("ContainsKey: {0}", b = sw.ElapsedTicks);
// ---------------------------------------------------------------------
sw.Restart();
for (int i = 0;i < size;++i)
try { val = values[lookups[i]]; }
catch { }
sw.Stop();
Console.WriteLine("try-catch: {0}", c = sw.ElapsedTicks);
// ---------------------------------------------------------------------
Console.WriteLine();
durations.Add(new Tuple<long, long, long>(a, b, c));
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Finished evaluation .... Time distribution:");
Console.ForegroundColor = bu;
val = 10;
foreach (Tuple<long, long, long> d in durations)
{
long sum = d.Item1 + d.Item2 + d.Item3;
Console.WriteLine("Size: {0:D6}:", val);
Console.WriteLine("TryGetValue: {0:P2}, ContainsKey: {1:P2}, try-catch: {2:P2} - Total: {3:N}", (decimal)d.Item1 / sum, (decimal)d.Item2 / sum, (decimal)d.Item3 / sum, sum);
val *= MULTIPLIER;
}
Console.WriteLine();
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
命名空间控制台应用程序1
{
班级计划
{
静态void Main(字符串[]参数)
{
const int ENTRIES=10000,MAXVAL=15000,TRIALS=100000,乘数=2;
字典值=新字典();
随机r=新随机();
int[]查找=新的int[试验];
int-val;
dict.GetValueOrDefault("keyname")
(dict.GetValueOrDefault("keyname") ?? fallbackValue)
using System.Collections.Generic;
namespace Project.Common.Extensions
{
public static class DictionaryExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
TValue defaultValue = default(TValue))
{
if (dictionary.TryGetValue(key, out TValue value))
{
return value;
}
return defaultValue;
}
}
}
var dict = new Dictionary<string, string>();
string val = dict.GetValueOrDefault("theKey", "value used if theKey is not found in dict");
var dictObj = new Dictionary<string, object>();
object valObj = dictObj.GetValueOrDefault("nonexistent");
Debug.Assert(valObj == null);
val dictInt = new Dictionary<string, int>();
int valInt = dictInt.GetValueOrDefault("nonexistent");
Debug.Assert(valInt == 0);