迭代字典的最佳方法是什么?

时间:2008-09-26 18:20:06

标签: c# dictionary loops

我已经看到了几种不同的方法来迭代C#中的字典。有标准的方法吗?

33 个答案:

答案 0 :(得分:3273)

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

答案 1 :(得分:752)

如果您尝试在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及更高版本的功能,您也可以在此处使用密钥/值的确切类型)

答案 2 :(得分:125)

在某些情况下,您可能需要一个可以由for循环实现提供的计数器。为此,LINQ提供了ElementAt,它可以实现以下功能:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

答案 3 :(得分:83)

取决于您是否在关键或值之后......

来自MSDN Dictionary(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);
}

答案 4 :(得分:71)

一般来说,要求&#34;最好的方式&#34;没有特定的背景就像问什么是最好的颜色。

一方面,有很多颜色,没有最好的颜色。这取决于需要,也经常取决于口味。

另一方面,有许多方法可以在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)
}

您可以从这些示例中获得更多真实用例。 如果您不需要特定订单,只需坚持最直接的方式&#34; (见上文)!

答案 5 :(得分:46)

我会说foreach是标准方式,但显然取决于你在寻找什么

foreach(var kvp in my_dictionary) {
  ...
}

这就是你要找的东西吗?

答案 6 :(得分:35)

您也可以在大字典上尝试使用多线程处理。

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

答案 7 :(得分:25)

有很多选择。我个人最喜欢的是KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

您还可以使用键和值集合

答案 8 :(得分:25)

我很欣赏这个问题已经有很多回复,但我想进行一些研究。

与迭代类似数组的东西相比,迭代字典可能会相当慢。在我的测试中,对数组的迭代花费了0.015003秒,而对字典的迭代(具有相同数量的元素)花费了0.0365073秒,这是2.4倍的长度!虽然我看到了更大的差异。为了进行比较,List介于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方法要差很多。

答案 9 :(得分:13)

C# 7.0 introduced 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}");
}

enter image description here

答案 10 :(得分:13)

正如对此answer所指出的那样,KeyValuePair<TKey, TValue>从.NET Core 2.0,.NET Standard 2.1和.NET Framework 5.0(预览版)开始实现Deconstruct方法。

通过这种方式,可以以KeyValuePair不可知的方式遍历字典:

var dictionary = new Dictionary<int, string>();

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}

答案 11 :(得分:11)

您建议在下面进行迭代

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

仅供参考,如果值为object类型,则foreach不起作用。

答案 12 :(得分:10)

使用.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;
    }
}

答案 13 :(得分:8)

迭代字典的最简单形式:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}

答案 14 :(得分:8)

使用 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的{​​{1}}转换为强类型IDictionary<TKey, TValue>,从而可以使用这种新的舒适语法。

它将所需的字典条目转换为tuple,因此它不会将整个字典转换为tuples,因此没有与此相关的性能问题。

与直接使用tuples相比,使用tuple创建KeyValuePair的扩展方法只需花费很少的费用,如果您要分配KeyValuePair&,这不应该是一个问题无论如何,#39; s属性KeyValue到新的循环变量。

在实践中,这种新语法非常适合大多数情况,除了低级超高性能方案,您仍然可以选择不在特定位置使用它。

检查出来:MSDN Blog - New features in C# 7

答案 15 :(得分:6)

有时,如果您只需要枚举值,请使用字典的值集合:

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

答案 16 :(得分:5)

我在MSDN上的DictionaryBase类的文档中找到了这个方法:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

这是我在从DictionaryBase继承的类中唯一能够正常运行的。

答案 17 :(得分:3)

从C#7开始,您可以将对象解构为变量。我相信这是迭代字典的最佳方法。

示例:

KeyValuePair<TKey, TVal>上创建可对其进行解构的扩展方法:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = 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}");
}

答案 18 :(得分:3)

根据MSDN上的官方文档,迭代字典的标准方法是:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}

答案 19 :(得分:3)

我将利用.NET 4.0+并为最初接受的答案提供更新的答案:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}

答案 20 :(得分:2)

如果说,您希望默认迭代值集合,我相信您可以实现IEnumerable&lt;&gt;,其中T是字典中值对象的类型,“this”是Dictionary。

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

答案 21 :(得分:1)

我知道这是一个非常老的问题,但是我创建了一些可能有用的扩展方法:

    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););

答案 22 :(得分:1)

只想添加我的2美分,因为大多数答案都与foreach-loop有关。 请看下面的代码:

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));
});

Altought这会额外调用'.ToList()',可能会有轻微的性能提升(正如foreach vs someList.Foreach(){}所指出的那样), 特别是在使用大字典并且并行运行时,没有选择/根本没有效果。

另外,请注意,您无法为foreach循环中的“Value”属性赋值。另一方面,您也可以操纵'Key',可能会在运行时遇到麻烦。

当您只想“读取”键和值时,您也可以使用IEnumerable.Select()。

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

答案 23 :(得分:1)

我写了一个扩展来循环字典。

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));

答案 24 :(得分:1)

var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

答案 25 :(得分:0)

<强>词典&LT; TKey,TValue&gt; 它是c#中的泛型集合类,它以键值格式存储数据.Key必须是唯一的,它不能为null,而值可以是重复的和null。作为每个项目在字典被视为KeyValuePair&lt; TKey,TValue&gt;表示密钥及其价值的结构。因此我们应该采用元素类型KeyValuePair&lt; TKEY的,TValue&GT;在元素的迭代过程中。以下是示例。

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);
}

答案 26 :(得分:0)

三种遍历字典的方法。

  

方法1-使用for循环

for (int i = 0; i < DemotestDictionary.Count; i++ )
 {
 int key = i;
 string value = DemotestDictionary[i];
 // Do something here
 }
  

方法2:使用foreach迭代

foreach (var entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
 // Do something here
 } 
  

使用KeyValuePair进行方法3-foreach迭代

foreach (KeyValuePair<int, string> entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
// Do something here
 }

答案 27 :(得分:0)

foreach最快,如果仅迭代___.Values,它也更快

enter image description here

答案 28 :(得分:0)

当然最好的答案是:如果您打算迭代它,请不要使用精确的字典-正如 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<,>): 这是相当令人惊讶的,但有一点被引用,但清洁编码的基本原则: “表达意图!” Robert C. Martin 在“Clean Code”中写道:“选择能揭示意图的名字”。显然,这太弱了。 “通过每个编码决定表达(揭示)意图”会更好。我的措辞。我缺乏一个好的第一个来源,但我确定有... 一个相关的原则是“Principle of least surprise”(=Principle of Least Astonishment)。

为什么这与迭代字典有关? 选择字典表达了选择一种数据结构的意图,该数据结构用于通过键查找数据。现在 .NET 中有很多替代方案,如果你想遍历键/值对,你不需要这个拐杖。

此外:如果你迭代某些东西,你必须揭示一些关于物品是如何(将)订购的以及如何被订购的! AFAIK,Dictionary 没有关于排序的规范(仅限于实现特定的约定)。 有哪些替代方案?

TLDR:
SortedList:如果您的集合不会变得太大,一个简单的解决方案是使用 SortedList<,> 它还为您提供键/值对的完整索引。

Microsoft 有一篇关于提及和解释拟合集合的长篇文章:
Keyed collection

提到最重要的:KeyedCollection<,> 和 SortedDictionary<,> 。 SortedDictionary<,> 比 SortedList 快一点,如果它变大,则用于 oly 插入,但缺少索引,并且只有在 O(log n) 插入操作优先于其他操作时才需要。如果您确实需要 O(1) 进行插入并接受较慢的迭代作为交换,则必须使用简单的 Dictionary<,>。 显然,没有一种数据结构对于每种可能的操作都是最快的。

此外还有 ImmutableSortedDictionary<,>.

如果一种数据结构不完全符合您的需要,则从 Dictionary<,> 甚至新的 ConcurrentDictionary<,> 派生,并添加显式迭代/排序函数!

答案 29 :(得分:-1)

如果要使用for循环,可以执行以下操作:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }

答案 30 :(得分:-2)

除了排名最高的帖子外,还讨论了使用之间的问题

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
}

答案 31 :(得分:-3)

简单的linq

 Dictionary<int, string> dict = new Dictionary<int, string>();
 dict.Add(1, "American Ipa");
 dict.Add(2, "Weiss");
 dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );

答案 32 :(得分:-4)

字典是特殊列表,而列表中的每个值都有一个键    这也是一个变量。字典的一个很好的例子是电话簿。

   Dictionary<string, long> phonebook = new Dictionary<string, long>();
    phonebook.Add("Alex", 4154346543);
    phonebook["Jessica"] = 4159484588;

请注意,在定义字典时,我们需要提供泛型    定义有两种类型 - 键的类型和值的类型。在这种情况下,键是一个字符串,而值是一个整数。

还有两种方法可以使用括号运算符或使用Add方法将单个值添加到字典中。

要检查字典中是否有某个键,我们可以使用ContainsKey方法:

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

if (phonebook.ContainsKey("Alex"))
{
    Console.WriteLine("Alex's number is " + phonebook["Alex"]);
}

要从字典中删除项目,我们可以使用Remove方法。通过键从字典中删除项目非常快速且非常高效。使用其值从List中删除项目时,该过程缓慢且效率低,与字典删除功能不同。

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);