我已经看到了几种在 C# 中迭代字典的不同方法。有标准方法吗?
31 回答
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
如果您尝试在 C# 中使用通用字典,就像在另一种语言中使用关联数组一样:
foreach(var item in myDictionary)
{
foo(item.Key);
bar(item.Value);
}
或者,如果您只需要遍历键集合,请使用
foreach(var item in myDictionary.Keys)
{
foo(item);
}
最后,如果您只对这些值感兴趣:
foreach(var item in myDictionary.Values)
{
foo(item);
}
(请注意,var
关键字是可选的 C# 3.0 及更高版本功能,您也可以在此处使用您的键/值的确切类型)
在某些情况下,您可能需要一个可能由 for 循环实现提供的计数器。为此,LINQ 提供ElementAt
了以下功能:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
取决于您是追求键还是值...
从 MSDNDictionary(TKey, TValue)
类描述:
// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
openWith.Values;
// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
Console.WriteLine("Value = {0}", s);
}
// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
openWith.Keys;
// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
Console.WriteLine("Key = {0}", s);
}
一般来说,在没有特定上下文的情况下询问“最好的方式”就像问 什么是最好的颜色?
一方面,有很多颜色,没有最好的颜色。这取决于需要,通常也取决于口味。
另一方面,有很多方法可以在 C# 中迭代字典,并且没有最好的方法。这取决于需要,通常也取决于口味。
最直接的方法
foreach (var kvp in items)
{
// key is kvp.Key
doStuff(kvp.Value)
}
如果您只需要值(允许调用它item
,比 更具可读性kvp.Value
)。
foreach (var item in items.Values)
{
doStuff(item)
}
如果您需要特定的排序顺序
一般来说,初学者对字典的枚举顺序感到惊讶。
LINQ 提供了一种简洁的语法,允许指定顺序(以及许多其他内容),例如:
foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
// key is kvp.Key
doStuff(kvp.Value)
}
同样,您可能只需要该值。LINQ 还提供了一个简洁的解决方案:
- 直接在值上迭代(允许调用它
item
,比 更具可读性kvp.Value
) - 但按键排序
这里是:
foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
doStuff(item)
}
您可以从这些示例中做更多的实际用例。如果您不需要特定的顺序,只需坚持“最直接的方式”(见上文)!
我想说foreach
的是标准方式,尽管它显然取决于你在寻找什么
foreach(var kvp in my_dictionary) {
...
}
那是你要找的吗?
C# 7.0引入了Deconstructors,如果您使用的是.NET Core 2.0+应用程序,该结构KeyValuePair<>
已经Deconstruct()
为您包含了一个。所以你可以这样做:
var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
Console.WriteLine($"Item [{key}] = {value}");
}
您也可以在用于多线程处理的大字典上尝试此操作。
dictionary
.AsParallel()
.ForAll(pair =>
{
// Process pair.Key and pair.Value here
});
我很欣赏这个问题已经有很多回应,但我想进行一些研究。
与迭代数组之类的东西相比,迭代字典可能会相当慢。在我的测试中,对数组的迭代耗时 0.015003 秒,而对字典(具有相同数量的元素)的迭代耗时 0.0365073 秒,是 2.4 倍!虽然我看到了更大的差异。为了比较,列表介于 0.00215043 秒之间。
然而,这就像比较苹果和橘子。我的观点是迭代字典很慢。
字典针对查找进行了优化,因此考虑到这一点,我创建了两种方法。一个简单地做一个 foreach,另一个迭代键然后查找。
public static string Normal(Dictionary<string, string> dictionary)
{
string value;
int count = 0;
foreach (var kvp in dictionary)
{
value = kvp.Value;
count++;
}
return "Normal";
}
这个加载键并对其进行迭代(我也尝试将键拉入字符串 [] 但差异可以忽略不计。
public static string Keys(Dictionary<string, string> dictionary)
{
string value;
int count = 0;
foreach (var key in dictionary.Keys)
{
value = dictionary[key];
count++;
}
return "Keys";
}
在这个例子中,正常的 foreach 测试需要 0.0310062,而密钥版本需要 0.2205441。加载所有键并遍历所有查找显然要慢很多!
对于最终测试,我已经执行了十次迭代,看看在此处使用密钥是否有任何好处(此时我只是好奇):
这是 RunTest 方法,如果它可以帮助您可视化正在发生的事情。
private static string RunTest<T>(T dictionary, Func<T, string> function)
{
DateTime start = DateTime.Now;
string name = null;
for (int i = 0; i < 10; i++)
{
name = function(dictionary);
}
DateTime end = DateTime.Now;
var duration = end.Subtract(start);
return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}
在这里,正常的 foreach 运行耗时 0.2820564 秒(大约是单次迭代耗时的十倍——如您所料)。对键的迭代花费了 2.2249449 秒。
编辑添加: 阅读其他一些答案让我质疑如果我使用 Dictionary 而不是 Dictionary 会发生什么。在这个例子中,数组耗时 0.0120024 秒,列表耗时 0.0185037 秒,字典耗时 0.0465093 秒。可以合理地预期数据类型会影响字典的速度。
我的结论是什么?
- 如果可以,请避免迭代字典,它们比迭代具有相同数据的数组要慢得多。
- 如果您确实选择遍历字典,请不要太聪明,尽管速度较慢,但您可能会比使用标准 foreach 方法做得更糟。
有很多选择。我个人最喜欢的是 KeyValuePair
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
// Do some interesting things
}
您还可以使用键和值集合
正如已在此答案中指出的那样,KeyValuePair<TKey, TValue>
实现了Deconstruct
从 .NET Core 2.0、.NET Standard 2.1 和 .NET Framework 5.0(预览版)开始的方法。
KeyValuePair
有了这个,就可以以不可知的方式遍历字典:
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
用.NET Framework 4.7
一个可以用分解
var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
Console.WriteLine(fruit + ": " + number);
}
要使此代码在较低的 C# 版本上工作,请在System.ValueTuple NuGet package
某处添加和编写
public static class MyExtensions
{
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
out T1 key, out T2 value)
{
key = tuple.Key;
value = tuple.Value;
}
}
从 C# 7 开始,您可以将对象解构为变量。我相信这是迭代字典的最佳方式。
例子:
创建一个KeyValuePair<TKey, TVal>
解构它的扩展方法:
public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
key = pair.Key;
value = pair.Value;
}
Dictionary<TKey, TVal>
以下列方式迭代任何
// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();
// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
Console.WriteLine($"{key} : {value}");
}
您建议在下面进行迭代
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
仅供参考,foreach
如果值是对象类型,则不起作用。
迭代字典的最简单形式:
foreach(var item in myDictionary)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value);
}
使用C# 7将此扩展方法添加到解决方案的任何项目中:
public static class IDictionaryExtensions
{
public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
this IDictionary<TKey, TValue> dict)
{
foreach (KeyValuePair<TKey, TValue> kvp in dict)
yield return (kvp.Key, kvp.Value);
}
}
并使用这个简单的语法
foreach (var(id, value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
或者这个,如果你喜欢
foreach ((string id, object value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
代替传统的
foreach (KeyValuePair<string, object> kvp in dict)
{
string id = kvp.Key;
object value = kvp.Value;
// your code using 'id' and 'value'
}
扩展方法将KeyValuePair
of yourIDictionary<TKey, TValue>
转换为强类型tuple
,允许您使用这种新的舒适语法。
它将所需的字典条目转换为tuples
,因此它不会将整个字典转换为tuples
,因此不存在与此相关的性能问题。
tuple
与直接使用相比,调用扩展方法来创建 a 的成本很小,如果您要分配的属性和新的循环变量KeyValuePair
,这应该不是问题。KeyValuePair
Key
Value
在实践中,这种新语法非常适合大多数情况,除了低级超高性能场景,您仍然可以选择在特定位置不使用它。
看看这个:MSDN 博客 - C# 7 中的新功能
我知道这是一个非常古老的问题,但我创建了一些可能有用的扩展方法:
public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
{
foreach (KeyValuePair<T, U> p in d) { a(p); }
}
public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
{
foreach (T t in k) { a(t); }
}
public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
{
foreach (U u in v) { a(u); }
}
这样我就可以编写如下代码:
myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););
我在 MSDN 上 DictionaryBase 类的文档中找到了这个方法:
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
这是我唯一能够在从 DictionaryBase 继承的类中正常运行的功能。
有时,如果您只需要枚举值,请使用字典的值集合:
foreach(var value in dictionary.Values)
{
// do something with entry.Value only
}
这篇文章报告说这是最快的方法: http ://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html
如果你想使用 for 循环,你可以这样做:
var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
var key= keyList[i];
var value = dictionary[key];
}
我将利用 .NET 4.0+ 并为最初接受的答案提供更新的答案:
foreach(var entry in MyDic)
{
// do something with entry.Value or entry.Key
}
根据 MSDN 上的官方文档,迭代字典的标准方法是:
foreach (DictionaryEntry entry in myDictionary)
{
//Read entry.Key and entry.Value here
}
我写了一个扩展来遍历字典。
public static class DictionaryExtension
{
public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
action(keyValue.Key, keyValue.Value);
}
}
}
然后你可以打电话
myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
如果说,你想默认迭代values集合,我相信你可以实现IEnumerable<>,其中T是字典中values对象的类型,“this”是字典。
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
有几种方法可以在 C# 中迭代字典,但我发现最好和最简单的方法是使用foreach。
foreach(KeyValuePair<string, string> entry in mDictionary)
{
// Your coding for Value & Key ...
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
Dictionary< TKey, TValue >是c#中的一个通用集合类,它以键值格式存储数据。key必须唯一且不能为null,而value可以重复且为null。因为字典中的每一项都是视为 KeyValuePair< TKey, TValue > 结构,表示一个键及其值。因此我们应该在元素的迭代过程中采用元素类型 KeyValuePair<TKey, TValue>。下面是示例。
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");
foreach (KeyValuePair<int, string> item in dict)
{
Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
只是想加我的 2 美分,因为大多数答案与 foreach 循环有关。请看下面的代码:
Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();
//Add some entries to the dictionary
myProductPrices.ToList().ForEach(kvP =>
{
kvP.Value *= 1.15;
Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});
尽管这增加了对“.ToList()”的额外调用,但可能会有轻微的性能改进(如此处指出的foreach vs someList.Foreach(){}),尤其是在使用大型字典和并行运行时不会选项 / 根本没有效果。
另外,请注意,您将无法为 foreach 循环内的“值”属性分配值。另一方面,您也可以操纵“密钥”,可能会在运行时给您带来麻烦。
当您只想“读取”键和值时,您还可以使用 IEnumerable.Select()。
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
最好的答案当然是:想一想,如果您打算迭代它,是否可以使用比字典更合适的数据结构- 正如 Vikas Gupta 在问题下的讨论(开始)中已经提到的那样。但是,作为整个线程的讨论仍然缺乏令人惊讶的好选择。一种是:
SortedList<string, string> x = new SortedList<string, string>();
x.Add("key1", "value1");
x.Add("key2", "value2");
x["key3"] = "value3";
foreach( KeyValuePair<string, string> kvPair in x )
Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");
为什么可以争论迭代字典的代码味道(例如通过 foreach(KeyValuePair<,>) ?
Clean Coding 的一个基本原则:“表达意图! ” Robert C. Martin 在“Clean Code”中写道:“选择显示意图的名称”。显然,仅命名太弱了。“表达(揭示)每个编码决策的意图”更好地表达了它。
为什么这与迭代字典有关?选择字典表达了选择数据结构的意图,该数据结构主要用于通过 key 查找数据。如今,.NET 中有很多替代方案,如果您想遍历键/值对,您可以选择其他东西。
此外:如果您对某些内容进行迭代,则必须揭示有关(将要)如何订购和预期如何订购这些项目的信息!尽管 Dictionary 的已知实现按照添加的项目的顺序对键集合进行排序 - AFAIK,但 Dictionary 没有关于排序的保证规范(有吗?)。
但是有什么替代方案?
TLDR:
SortedList:如果您的集合没有变得太大,一个简单的解决方案是使用 SortedList<,> 它还可以为您提供键/值对的完整索引。
微软有一篇很长的文章提到和解释拟合集合:
Keyed collection
提一下最重要的:KeyedCollection <,> 和 SortedDictionary<,> 。 SortedDictionary <,> 比 SortedList 快一点,仅当它变大时才插入,但缺少索引,并且仅当 O(log n) 插入优先于其他操作时才需要。如果您确实需要 O(1) 来插入并接受较慢的迭代作为交换,您必须使用简单的 Dictionary<,>。显然,对于所有可能的操作,没有最快的数据结构。
此外还有ImmutableSortedDictionary <,>。
如果一个数据结构不是您所需要的,那么从 Dictionary<,> 甚至新的ConcurrentDictionary <,> 派生并添加显式迭代/排序函数!
除了在使用之间进行讨论的排名最高的帖子之外
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
或者
foreach(var entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
最完整的如下,因为你可以从初始化中看到字典类型,kvp 是 KeyValuePair
var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x
foreach(var kvp in myDictionary)//iterate over dictionary
{
// do something with kvp.Value or kvp.Key
}