我已经看到了几种不同的方法来迭代C#中的字典。有没有标准的方法?
我已经看到了几种不同的方法来迭代C#中的字典。有没有标准的方法?
Answers:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var entry
在这种情况下,使用效果更好,因此,我的确将回答这个问题而不是上面的观点。
var
在您不知道类型时使用,通常是不好的做法。
var
只有在编译时知道类型的情况下才起作用。如果Visual Studio知道类型,那么您也可以找到它。
如果您试图像在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及更高版本的功能,您也可以在此处使用键/值的确切类型)
myDictionary
(除非这是实际名称)。我认为在类型明显的情况下使用var很好,例如,var x = "some string"
但在类型不明显的情况下使用var则好,我认为这是懒惰的编码,会损害代码阅读器/审阅者
var
我认为应该谨慎使用。特别是在这里,它不是建设性的:类型KeyValuePair
可能与问题有关。
var
有一个独特的目的,我不认为这是“句法”糖。有目的地使用它是一种适当的方法。
在某些情况下,您可能需要一个由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
O(n)运算吗?
.ElementAt
在此上下文中使用字典可能会导致细微的错误。更为严重的是Arturo的观点。您将迭代字典dictionary.Count + 1
时间,导致只应为O(n)的操作的O(n ^ 2)复杂度。如果您确实需要索引(如果这样做,那么您可能首先使用了错误的集合类型),则应该进行迭代dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
而不是.ElementAt
在循环内使用。
取决于您要使用的是键还是值...
从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);
}
通常,在没有特定上下文的情况下询问“最佳方法”就像询问 最佳颜色是什么?
一方面,有很多颜色,没有最好的颜色。这取决于需求,通常也取决于口味。
另一方面,有很多方法可以迭代C#中的Dictionary,并且没有最佳方法。这取决于需求,通常也取决于口味。
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
而不是select子句。
Value
字段。我在这里看到的确切类型是IOrderedEnumerable<KeyValuePair<TKey, TValue>>
。也许您还有其他意思?您可以写完整的行来显示您的意思(并进行测试)吗?
items.Value
您的建议写了。对于您评论的第四部分,这Select()
是一种导致foreach
直接枚举字典中的值而不是键值对的方法。如果在某种Select()
情况下您不喜欢该示例,则可能更喜欢第三个代码部分。第四部分的要点是表明可以使用LINQ对集合进行预处理。
.Keys.Orderby()
您将迭代键列表。如果这就是您所需要的,那就好。如果需要值,则在循环中,您必须查询每个键上的字典以获取值。在许多情况下,它并没有什么实际意义。在高性能方案中,它将。就像我在答案开头写道:“有很多方法(...),没有最好的方法。这取决于需求,通常也取决于味道。”
我会说foreach是标准方式,尽管它显然取决于您要查找的内容
foreach(var kvp in my_dictionary) {
...
}
那是您要找的东西吗?
kvp
在字典和相关数据结构上进行迭代时,@ RenniePet 通常用于命名KeyValuePair实例foreach(var kvp in myDictionary){...
。
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}");
}
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
我感谢这个问题已经得到了很多答复,但我想进行一些研究。
与对数组之类的对象进行迭代相比,对字典的迭代可能会比较慢。在我的测试中,数组上的迭代花费了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";
}
这个加载了键并在它们上进行了迭代(我也尝试过将键拉入string [],但是差别可以忽略不计。
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秒。
编辑添加: 阅读其他一些答案使我怀疑如果我使用词典而不是词典会发生什么。在此示例中,数组花费0.0120024秒,列表花费0.0185037秒,字典花费0.0465093秒。可以合理预期数据类型会影响字典的运行速度。
我的结论是什么?
用.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;
}
}
ValueTuple
内置了。它可以作为nuget包用于早期版本。更重要的是,该Deconstruct
方法需要C#7.0+ 才能用作的解构函数var (fruit, number) in fruits
。
从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
如果值是object类型,则不起作用。
foreach
如果哪个值是类型,将不起作用object
?否则,这没有什么意义。
使用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
您的IDictionary<TKey, TValue>
转换为强类型tuple
,使您可以使用这种新的舒适语法。
它将所需的字典条目仅转换为tuples
,因此不会将整个字典转换为tuples
,因此没有与此相关的性能问题。
tuple
与KeyValuePair
直接使用直接调用相比,调用扩展方法来创建一个扩展仅需花费很少的钱,如果您要分配KeyValuePair
的属性Key
和Value
反正新的循环变量。
实际上,此新语法非常适合大多数情况,但低级超高性能方案除外,在这种情况下,您仍然可以选择不直接在特定位置使用它。
kvp.Key
并kvp.Value
分别使用键和值。使用元组,您可以灵活地根据需要命名键和值,而无需在foreach块内使用其他变量声明。例如,您可以将键命名为factoryName
,将值命名为,models
当您获得嵌套循环(字典的字典)时,这尤其有用:代码维护变得容易得多。试一试!;-)
我知道这是一个非常老的问题,但是我创建了一些可能有用的扩展方法:
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););
有时,如果只需要枚举值,请使用字典的值集合:
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类的文档中找到此方法:
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
这是我唯一能够在从DictionaryBase继承的类中正常运行的函数。
Hashtable
的对象
ContainsKey()
该for
版本?这增加了您要比较的代码中不存在的额外开销。TryGetValue()
存在以替换该确切的“如果键存在,则用键获取项目”模式。此外,如果dict
包含从0
到的连续整数范围dictCount - 1
,您将知道索引器不会失败;否则,dict.Keys
这就是您应该迭代的内容。无论哪种方式,都不需要ContainsKey()
/ TryGetValue()
不需要。最后,请不要发布代码的屏幕截图。
我写了一个扩展来遍历字典。
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));
ForEach
一种您拥有的方法foreach (...) { }
。似乎不必要。
如果说,您希望默认情况下遍历值集合,我相信您可以实现IEnumerable <>,其中T是字典中values对象的类型,而“ this”是Dictionary。
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
我只想加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(){}),尤其是在使用大型Dictionary并并行运行时选项/完全无效。
另外,请注意,您将无法在foreach循环内将值分配给'Value'属性。另一方面,您也可以操纵“键”,可能会在运行时遇到麻烦。
当您只想“读取”键和值时,也可以使用IEnumerable.Select()。
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
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
)用于的事物类型(尤其是具有副作用的操作)不在Linq的范围内,包括Select
。在aggregateObjectCollection
实际枚举之前,lambda不会运行。如果将此答案视为“第一条道路”(即在直道之前使用foreach
),则会鼓励不良作法。有时,Linq操作可能在迭代字典之前很有用,但是并不能解决所要求的问题。
Dictionary <TKey,TValue>这是c#中的通用集合类,它以键值格式存储数据。键必须是唯一的,并且不能为null,而值可以重复和为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);
}
如果要使用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循环...”,我将以“ 不要使用for
循环” 来反驳。
foreach (var pair in dictionary.ToArray()) { }
。不过,我认为最好在答案中阐明一个人想要使用此代码的特定场景以及这样做的含义。
简单的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}") );
除了排名最高的帖子外,还讨论了使用
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
}