簡體   English   中英

如何遍歷字典?

[英]How to iterate over a dictionary?

我已經看到了幾種在 C# 中迭代​​字典的不同方法。 有標准方法嗎?

"

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

取決於您是在追求鍵還是在追求值...

來自 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# 中有很多方法可以迭代字典,但沒有最好的方法。 這取決於需要,通常也取決於口味。

最直接的方法

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

這個加載鍵並迭代它們(我也嘗試將鍵拉入 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 秒。 期望數據類型對字典慢多少有所不同是合理的。

我的結論是什么?

  • 如果可以,請避免迭代字典,它們比迭代具有相同數據的數組慢得多。
  • 如果您確實選擇迭代字典,請不要嘗試太聰明,盡管速度較慢,但​​與使用標准 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>實現了一個從 .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)
{
    // ...
}

使用.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是最快的,如果你只迭代___.Values ,它也會更快

在此處輸入圖片說明

迭代字典的最簡單形式:

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'
}


擴展方法將您的IDictionary<TKey, TValue>KeyValuePair轉換為強類型tuple ,允許您使用這種新的舒適語法。

它僅將所需的字典條目轉換為tuples ,因此它不會將整個字典轉換為tuples ,因此沒有與此相關的性能問題。

與直接使用KeyValuePair相比,調用用於創建tuple的擴展方法只有很小的成本,如果您將KeyValuePair的屬性KeyValue分配給新的循環變量,這應該不是問題。

在實踐中,這種新語法非常適合大多數情況,除了低級別的超高性能場景,您仍然可以選擇在特定位置不使用它。

查看: 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););

有時,如果您只需要枚舉值,請使用字典的值集合:

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 的類中正確運行的唯一一個。

我將利用 .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”是一個Dictionary。

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

如果你想使用 for 循環,你可以這樣做:

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

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

Dictionary<TKey, TValue>是c#中的泛型集合類,以鍵值格式存儲數據。鍵必須唯一,不能為空,值可以重復,為空。因為字典中的每一項都是被視為表示鍵及其值的 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-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 } );

最好的答案當然是:如果你打算迭代它,不要使用精確的字典- 正如 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”中寫道:“選擇能揭示意圖的名稱”。顯然這太弱了。“在每個編碼決定中表達(揭示)意圖”會更好。我的措辭。我缺乏一個好的第一來源但我確信有......一個相關的原則是“ 最小驚訝 原則”(= 最小驚訝原則)。

為什么這與迭代字典有關? 選擇字典表達了選擇數據結構的意圖,該數據結構用於通過 key 查找數據 現在 .NET 中有很多替代方案,如果您想遍歷不需要這個拐杖的鍵/值對。

此外:如果你迭代某些東西,你必須揭示一些關於(將)如何訂購和預計如何訂購物品的信息! AFAIK,Dictionary 沒有關於排序的規范(僅限於實現特定的約定)。 有哪些替代方案?

域名注冊地址:
SortedList :如果您的集合沒有變得太大,一個簡單的解決方案是使用 SortedList<,> ,它還為您提供鍵/值對的完整索引。

微軟有一篇關於提及和解釋擬合集合的長文章:
鍵控集合

提到最重要的: KeyedCollection <,> 和 SortedDictionary<,> 。 SortedDictionary <,> 在插入時比 SortedList 快一點,如果它變大,但缺少索引,並且只有在 O(log n) 插入操作優先於其他操作時才需要。 如果您確實需要 O(1) 進行插入並接受較慢的迭代作為交換,則必須使用簡單的 Dictionary<,>。 顯然,對於每種可能的操作,都沒有最快的數據結構。

此外還有ImmutableSortedDictionary <,>。

如果一種數據結構不是您所需要的,那么從 Dictionary<,> 甚至新的ConcurrentDictionary <,> 派生,並添加顯式迭代/排序函數!

詞典是特殊的列表,而列表中的每個值都有一個鍵,該鍵也是一個變量。 詞典的一個很好的例子是電話簿。

   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方法。 通過其鍵從字典中刪除項目既快速又高效。 當使用列表的值從列表中刪除項目時,該過程緩慢且效率低下,這與字典的“刪除”功能不同。

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

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

除了在使用之間進行討論的最高級別帖子之外

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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM