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

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


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


如果你想在c#中使用泛型字典,就像在另一种语言中使用关联数组一样:

1
2
3
4
5
foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

或者,如果只需要遍历键的集合,则使用

1
2
3
4
foreach(var item in myDictionary.Keys)
{
  foo(item);
}

最后,如果你只对价值观感兴趣:

1
2
3
4
foreach(var item in myDictionary.Values)
{
  foo(item);
}

(注意var关键字是一个可选的c# 3.0及以上特性,您也可以在这里使用键/值的确切类型)


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

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


这取决于你是想要键还是值…

从MSDN Dictionary(TKey, TValue)类描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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#中有很多遍历字典的方法,但没有最好的方法。这取决于需求,也常常取决于口味。

最直接的方式

1
2
3
4
5
foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

如果只需要该值(允许将其命名为item,比kvp.Value更具可读性)。

1
2
3
4
foreach (var item in items.Values)
{
    doStuff(item)
}

如果您需要一个特定的排序顺序

一般来说,初学者会对字典的枚举顺序感到惊讶。

LINQ提供了一个简洁的语法,允许指定顺序(和许多其他事情),例如:

1
2
3
4
5
foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

同样,您可能只需要值。LINQ还提供了一个简洁的解决方案:

直接迭代该值(允许将其称为item,比kvp.Value更具可读性)但按键排序

这里是:

1
2
3
4
foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

您可以从这些示例中完成更多的实际用例。如果您不需要特定的顺序,只需坚持"最直接的方式"(见上文)!


我认为foreach是标准的方法,不过这显然取决于您要查找的内容

1
2
3
foreach(var kvp in my_dictionary) {
  ...
}

这就是你要找的吗?


您也可以在用于多线程处理的大型字典中尝试这种方法。

1
2
3
4
5
6
dictionary
.AsParallel()
.ForAll(pair =>
{
    // Process pair.Key and pair.Value here
});


我很感激这个问题已经得到了很多回应,但我想再做一些研究。

与遍历数组之类的东西相比,遍历字典可能相当慢。在我的测试中,一个数组上的迭代花费0.015003秒,而一个字典(具有相同数量的元素)上的迭代花费0.0365073秒,这是它的2.4倍!尽管我看到了更大的差异。作为比较,列表的时间在0.00215043秒之间。

然而,这就像比较苹果和桔子一样。我的观点是遍历字典很慢。

字典是为查找而优化的,因此考虑到这一点,我创建了两个方法。一个简单地执行foreach,另一个迭代键然后查找。

1
2
3
4
5
6
7
8
9
10
11
12
public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return"Normal";
}

这一个加载键并迭代它们(我也尝试过将键拖放到string[]中,但是差异可以忽略不计。

1
2
3
4
5
6
7
8
9
10
11
12
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。加载所有键并遍历所有查找显然要慢得多!

对于最后的测试,我已经执行了我的迭代10次,看看使用这里的键是否有任何好处(此时我只是好奇):

下面是RunTest方法,如果它能帮助您可视化发生了什么。

1
2
3
4
5
6
7
8
9
10
11
12
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秒(大约是单个迭代所需时间的10倍——正如您所期望的那样)。键的迭代花费了2.2249449秒。

编辑添加:阅读其他一些答案让我思考如果我用字典而不是字典会发生什么。在本例中,数组花费0.0120024秒,列表花费0.0185037秒,字典花费0.0465093秒。可以合理地预期,数据类型会影响字典的运行速度。

我的结论是什么?

如果可以,避免在字典上迭代,它们比在包含相同数据的数组上迭代要慢得多。如果您选择在字典上迭代,不要太聪明,尽管比使用标准的foreach方法要慢很多。


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

1
2
3
4
5
6
7
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

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

您还可以使用键和值集合


使用.NET Framework 4.7可以使用分解

1
2
3
4
5
6
var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit +":" + number);
}

要使此代码在较低的c#版本上工作,请添加System.ValueTuple NuGet package并在某处编写

1
2
3
4
5
6
7
8
9
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;
    }
}


您在下面建议进行迭代

1
2
3
4
5
6
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将不起作用。


c# 7.0引入了解构器,如果您正在使用. net Core 2.0+应用程序,结构KeyValuePair<>已经为您包含了一个Deconstruct()。所以你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
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


使用c# 7,将此扩展方法添加到解决方案的任何项目中:

1
2
3
4
5
6
7
8
9
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);
    }
}

使用这个简单的语法

1
2
3
4
foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}

或者这个,如果你喜欢的话

1
2
3
4
foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}

取代了传统

1
2
3
4
5
6
7
foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}

扩展方法将您的IDictionaryKeyValuePair转换为强类型的tuple,允许您使用这种新的舒适语法。

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

与直接使用KeyValuePair相比,调用用于创建tuple的扩展方法只需要很少的开销,如果要将KeyValuePair的属性KeyValue分配给新的循环变量,那么这应该不是问题。

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

看看这个:MSDN博客- c# 7的新功能


迭代字典的最简单形式:

1
2
3
4
5
foreach(var item in myDictionary)
{
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}


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

1
2
3
4
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


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

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

这是我唯一能够在继承自DictionaryBase的类中正确运行的函数。


我将利用。net 4.0+的优势,为最初被接受的答案提供一个更新的答案:

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

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

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


如果您想在默认情况下迭代values集合,我相信您可以实现IEnumerable<>,其中T是字典中values对象的类型,"this"是字典。

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


从c# 7开始,您可以将对象分解为变量。我认为这是遍历字典的最佳方法。

例子:

KeyValuePair上创建一个扩展方法来解构它:

1
2
3
4
5
public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = pair.Value;
}

按照以下方式迭代任何Dictionary

1
2
3
4
5
6
7
8
// 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}");
}

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

1
2
3
4
5
6
7
8
9
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对someList.Foreach(){}),特别是当使用大型字典并并行运行时,没有选项/根本不会有效果。

另外,请注意,您不能为foreach循环中的"Value"属性赋值。另一方面,您也可以操作"Key",这可能会在运行时给您带来麻烦。

如果只想"读取"键和值,还可以使用IEnumerable.Select()。

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


我写了一个扩展来遍历字典。

1
2
3
4
5
6
7
8
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);
        }
    }
}

然后你可以打电话

1
myDictionary.ForEach((x,y) => Console.WriteLine(x +" -" + y));


1
2
3
4
5
6
7
var dictionary = new Dictionary<string, int>
{
    {"Key", 12 }
};

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


字典< TKey, ?它是c#中的一个通用集合类,它以键值格式存储数据。键必须是唯一的,不能为空,而值可以是重复的和空的。因为字典中的每一项都被视为KeyValuePair< TKey,?表示键及其值的TValue >结构。因此,我们应该取元素类型KeyValuePair< TKey,?TValue>在元素的迭代过程中。下面是示例。

1
2
3
4
5
6
7
8
9
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);
}


除了排名最高的职位,在哪里有使用之间的讨论

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

1
2
3
4
foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

最完整的代码如下所示,因为您可以从初始化中看到dictionary类型,kvp是KeyValuePair

1
2
3
4
5
6
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
}


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

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

注意,在定义字典时,我们需要提供一个泛型定义两个类型——键的类型和值的类型。在本例中,键是字符串,而值是整数。

还有两种方法可以将单个值添加到字典中,要么使用方括号操作符,要么使用Add方法。

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

1
2
3
4
5
6
7
8
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方法。通过键从字典中删除项是快速且非常有效的。当使用列表项的值从列表中删除项时,与dictionary Remove函数不同,该过程缓慢且效率低下。

1
2
3
4
5
6
Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

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