提问人:Jake Stewart 提问时间:9/27/2008 最后编辑:Mateen UlhaqJake Stewart 更新时间:5/6/2023 访问量:2342794
如何遍历字典?
How to iterate over a dictionary?
答:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
评论
for (name, address) in myDictionary.items()
foreach (var (key, value) in myDictionary)
我会说是标准方式,尽管这显然取决于您在寻找什么foreach
foreach(var kvp in my_dictionary) {
...
}
这就是你要找的吗?
如果您尝试在 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);
}
(请注意,关键字是可选的 C# 3.0 及更高版本功能,您也可以在此处使用键/值的确切类型)var
评论
var
在我看来,应该谨慎使用。特别是在这里,它不是建设性的:类型可能与问题相关。KeyValuePair
var
有一个独特的目的,我不相信它是“句法”糖。有目的地使用它是一种适当的方法。
foreach (var vehicle in line.Values) { start(vehicle); }
有很多选择。我个人最喜欢的是KeyValuePair
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
// Do some interesting things
}
还可以使用键和值集合
取决于你是在追求键还是值......
摘自MSDN词典(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);
}
比如说,你想默认遍历值集合,我相信你可以实现 IEnumerable<>,其中 T 是字典中值对象的类型,“this”是字典。
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
我在MSDN上的DictionaryBase类的文档中找到了此方法:
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
这是我唯一能够在继承自 DictionaryBase 的类中正常运行的。
评论
Hashtable
在某些情况下,您可能需要一个可能由 for 循环实现提供的计数器。为此,LINQ 提供了 ElementAt
,它支持以下功能:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
评论
ElementAt
.ElementAt
dictionary.Count + 1
dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
.ElementAt
我很欣赏这个问题已经有很多回答,但我想做一些研究。
与遍历数组之类的东西相比,遍历字典可能相当慢。在我的测试中,对数组的迭代需要 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 秒(大约是单次迭代所用时间的 10 倍 - 正如你所期望的那样)。键的迭代耗时 2.2249449 秒。
编辑添加:阅读其他一些答案让我质疑如果我使用 Dictionary 而不是 Dictionary 会发生什么。在此示例中,数组花费了 0.0120024 秒,列表花费了 0.0185037 秒,字典花费了 0.0465093 秒。可以合理地预期数据类型会对字典的速度产生影响。
我的结论是什么?
- 如果可以的话,避免遍历字典,它们比遍历具有相同数据的数组要慢得多。
- 如果你选择迭代字典,不要试图太聪明,尽管速度较慢,但你可能会比使用标准的foreach方法做得更糟糕。
评论
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
评论
AggregateObject
KeyValuePair
foreach
Select
foreach
Select
aggregateObjectCollection
foreach
您也可以在大型词典上尝试此操作以进行多线程处理。
dictionary
.AsParallel()
.ForAll(pair =>
{
// Process pair.Key and pair.Value here
});
评论
一般来说,在没有特定上下文的情况下询问“最佳方式”就像询问什么是最好的颜色一样?
一方面,有很多颜色,没有最好的颜色。这取决于需求,通常也取决于口味。
另一方面,在 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)
}
您可以从这些示例中执行更多实际用例。 如果您不需要特定的订单,只需坚持“最直接的方式”(见上文)!
评论
.Values
Value
IOrderedEnumerable<KeyValuePair<TKey, TValue>>
items.Value
Select()
foreach
Select()
.Keys.Orderby()
只是想添加我的 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));
});
尽管如此,这又增加了一个额外的调用“.ToList()',可能会有轻微的性能改进(正如这里指出的 foreach 与 someList.Foreach(){} ), 在空间上,当使用大型词典并行运行时,没有选择/根本没有效果。
另外,请注意,您将无法为 foreach 循环中的“Value”属性赋值。另一方面,您也可以操纵“密钥”,这可能会在运行时遇到麻烦。
当您只想“读取”键和值时,也可以使用 IEnumerable.Select()。
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
评论
foreach
.ForEach()
foreach
在 .NET Framework 4.7 中,可以使用分解
var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
Console.WriteLine(fruit + ": " + number);
}
若要使此代码在较低的 C# 版本上运行,请添加 NuGet 包并在某个位置编写System.ValueTuple
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;
}
}
评论
ValueTuple
Deconstruct
var (fruit, number) in fruits
使用 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'
}
扩展方法将 your 转换为强类型,允许您使用这种新的舒适语法。KeyValuePair
IDictionary<TKey, TValue>
tuple
它将所需的字典条目转换为 ,因此它不会将整个字典转换为 ,因此不存在与此相关的性能问题。tuples
tuples
与直接使用 相比,调用扩展方法创建 的开销很小,如果您无论如何都要将 的属性和新的循环变量赋值,这应该不是问题。tuple
KeyValuePair
KeyValuePair
Key
Value
在实践中,这种新语法非常适合大多数情况,但低级超高性能方案除外,在这些方案中,您仍然可以选择不在该特定位置使用它。
请查看此内容: MSDN 博客 - C# 7 中的新功能
评论
kvp.Key
kvp.Value
factoryName
models
从 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}");
}
评论
除了排名最高的帖子之外,还有使用
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
}
评论
C# 7.0 引入了构造函数,如果你使用的是 .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}");
}
评论
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
如果要使用循环,可以执行以下操作:for
var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
var key= keyList[i];
var value = dictionary[key];
}
评论
foreach
new List<string>(dictionary.Keys)
dictionary.Count
for
foreach (var pair in dictionary.ToArray()) { }
最好的答案当然是:想一想,如果你打算迭代它,如果你可以使用比字典更合适的数据结构——正如 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<,>)?
干净编码的基本原则: “表达意图!” 罗伯特·C·马丁(Robert C. Martin)在《干净的代码》(Clean Code)一书中写道:“选择揭示意图的名称”。显然,仅命名太弱了。“在每个编码决策中表达(揭示)意图”更好地表达了这一点。
一个相关的原则是“最小惊讶原则”(=最小惊讶原则)。
为什么这与遍历字典有关?选择字典表达了选择数据结构的意图,该结构主要用于按键查找数据。如今,.NET 中有很多替代方案,如果您想循环访问键/值对,则可以选择其他选项。
此外:如果你迭代一些东西,你必须揭示一些关于这些物品是如何(将被)排序和预期被排序的! 尽管 Dictionary 的已知实现按照添加项的顺序对键集合进行排序- AFAIK,字典没有关于订购的保证规范(有吗?
但是有什么替代方案呢?
TLDR:
SortedList:如果您的集合不是太大,一个简单的解决方案是使用 SortedList<,>,它还可以为您提供键/值对的完整索引。
Microsoft 有一篇关于提及和解释配件集合的长篇文章:
键控集合
提到最重要的: KeyedCollection<,> 和 SortedDictionary<,> 。SortedDictionary<,> 比 SortedList 快一点,仅在插入时才进行插入,但缺少索引,并且仅当用于插入的 O(log n) 优先于其他操作时才需要。如果你真的需要 O(1) 来插入并接受较慢的迭代作为交换,你必须使用简单的 Dictionary<,>。 显然,没有一种数据结构对于每个可能的操作都是最快的。
此外,还有 ImmutableSortedDictionary<,>。
如果一个数据结构不完全是你需要的,那么从Dictionary<,>甚至从新的ConcurrentDictionary<,>派生,并添加显式迭代/排序函数!
评论