Cách tốt nhất để lặp lại từ điển là gì?


2627

Tôi đã thấy một vài cách khác nhau để lặp lại từ điển trong C #. Có một cách tiêu chuẩn?


108
Tôi ngạc nhiên khi có nhiều câu trả lời cho câu hỏi này, cùng với một câu hỏi được nâng cấp 923 lần .. (sử dụng foreach ofcference) .. Tôi tranh luận, hoặc ít nhất là thêm rằng nếu bạn phải lặp lại từ điển, rất có thể bạn là sử dụng nó không đúng cách / không phù hợp .. Tôi đã phải đưa ra nhận xét này, bởi vì tôi đã thấy từ điển bị lạm dụng theo cách mà IMHO không phù hợp ... Có thể có những trường hợp hiếm khi bạn lặp lại từ điển, thay vì tra cứu, đó là những gì nó được thiết kế cho .. Xin hãy ghi nhớ điều đó, trước khi tự hỏi làm thế nào để lặp lại từ điển.
Vikas Gupta

74
@VikasGupta Bạn muốn đề xuất làm gì với bộ sưu tập các cặp khóa-giá trị khi bạn không biết khóa sẽ là gì?
nasch

26
@displayName Nếu bạn muốn làm gì đó với từng cặp khóa-giá trị nhưng không có tham chiếu đến các khóa được sử dụng để tra cứu giá trị, bạn sẽ lặp lại từ điển, phải không? Tôi chỉ chỉ ra rằng có thể đôi khi bạn muốn làm điều đó, mặc dù tuyên bố của Vikas rằng đây thường là cách sử dụng không chính xác.
18 giờ

34
Nói rằng việc sử dụng không chính xác ngụ ý rằng có một sự thay thế tốt hơn. Cái gì thay thế?
Kyle Delaney

40
VikasGupta đã sai, tôi có thể khẳng định rằng sau nhiều năm lập trình C # và C ++ hiệu suất cao trong các tình huống phi lý thuyết. Thực sự có nhiều trường hợp người ta sẽ tạo một từ điển, lưu trữ các cặp khóa-giá trị duy nhất và sau đó lặp lại các giá trị này, được chứng minh là có các khóa duy nhất trong bộ sưu tập. Tạo bất kỳ bộ sưu tập nào nữa là một cách thực sự không hiệu quả và tốn kém để tránh lặp từ điển. Vui lòng cung cấp một lựa chọn tốt như câu trả lời cho câu hỏi làm rõ quan điểm của bạn, nếu không bình luận của bạn là khá vô nghĩa.
sɐunıɔ qɐp

Câu trả lời:


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

29
Điều gì xảy ra nếu tôi không biết chính xác loại khóa / giá trị trong Từ điển. Sử dụng var entrylà tốt hơn trong trường hợp đó, và do đó tôi đã bỏ phiếu cho câu trả lời này trên giao diện thứ hai thay vì ở trên.
Ozair Kafray

93
@OzairKafray sử dụng varkhi bạn không biết loại nói chung là thực tiễn xấu.
Nate

52
Câu trả lời này là vượt trội vì Pablo không mặc định sử dụng "var" của bộ mã hóa lười biếng làm xáo trộn kiểu trả về.
MonkeyWbler 17/05/2016

119
@MonkeyWbler: Meh. Visual Studio biết loại này là gì; tất cả những gì bạn phải làm là di chuột qua biến để tìm hiểu.
Robert Harvey

31
Theo tôi hiểu, varchỉ hoạt động nếu loại được biết tại thời gian biên dịch. Nếu Visual Studio biết loại này thì bạn cũng có thể tìm hiểu.
Kyle Delaney

866

Nếu bạn đang cố gắng sử dụng Từ điển chung trong C # như bạn sẽ sử dụng một mảng kết hợp trong ngôn ngữ khác:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

Hoặc, nếu bạn chỉ cần lặp lại bộ sưu tập các phím, hãy sử dụng

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

Và cuối cùng, nếu bạn chỉ quan tâm đến các giá trị:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(Lưu ý rằng vartừ khóa là một tính năng tùy chọn C # 3.0 trở lên, bạn cũng có thể sử dụng loại khóa / giá trị chính xác của mình tại đây)


11
tính năng var được yêu cầu nhiều nhất cho khối mã đầu tiên của bạn :)
nawfal

27
Tôi đánh giá cao rằng câu trả lời này chỉ ra rằng bạn có thể lặp lại các khóa hoặc các giá trị một cách rõ ràng.
Rotsiser Mho

11
Tôi không thích việc sử dụng var ở đây. Cho rằng nó chỉ là đường cú pháp, tại sao sử dụng nó ở đây? Khi ai đó đang cố đọc mã, họ sẽ phải nhảy xung quanh mã để xác định loại myDictionary(trừ khi đó là tên thực tế của khóa học). Tôi nghĩ rằng sử dụng var là tốt khi loại này là rõ ràng, var x = "some string"nhưng khi nó không rõ ràng ngay lập tức tôi nghĩ rằng việc mã hóa lười biếng làm tổn thương người đọc / đánh giá mã
James Wierzba

8
varTheo tôi nên sử dụng một cách tiết kiệm. Đặc biệt ở đây, nó không mang tính xây dựng: loại KeyValuePaircó khả năng liên quan đến câu hỏi.
Sinjai

3
varcó một mục đích duy nhất và tôi không tin đó là đường 'cú pháp'. Sử dụng nó có mục đích là một cách tiếp cận phù hợp.
Joshua K

159

Trong một số trường hợp, bạn có thể cần một bộ đếm có thể được cung cấp bằng cách thực hiện vòng lặp for. Do đó, LINQ cung cấp ElementAtcho phép những điều sau đây:

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

21
Để sử dụng phương thức '.EuityAt', hãy nhớ: sử dụng System.Linq; Điều này không bao gồm trong fx. tự động tạo các lớp kiểm tra.
Tinia

14
Đây là cách để đi nếu bạn đang sửa đổi các giá trị được liên kết với các khóa. Mặt khác, một ngoại lệ được đưa ra khi sửa đổi và sử dụng foreach ().
Mike de Klerk

27
Không phải là ElementAtmột hoạt động O (n)?
Arturo Torres Sánchez

162
Câu trả lời này là hoàn toàn không xứng đáng với rất nhiều upvote. Một từ điển không có thứ tự ngầm, vì vậy sử dụng .ElementAttrong bối cảnh này có thể dẫn đến các lỗi tinh vi. Nghiêm trọng hơn nhiều là quan điểm của Arturo ở trên. Bạn sẽ lặp lại số dictionary.Count + 1lần từ điển dẫn đến độ phức tạp O (n ^ 2) cho một thao tác chỉ nên là O (n). Nếu bạn thực sự cần một chỉ mục (nếu có, có lẽ bạn đang sử dụng loại bộ sưu tập sai ở vị trí đầu tiên), bạn nên lặp lại dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})thay thế và không sử dụng .ElementAtbên trong vòng lặp.
tiêu

5
Hoạt động ElementAt - o (n)! Nghiêm túc? Đây là ví dụ về cách bạn không nên làm điều đó. Những upvote nhiều?
Mukesh Adhvaryu

96

Phụ thuộc vào việc bạn theo đuổi các khóa hay các giá trị ...

Từ Dictionary(TKey, TValue)mô tả Lớp MSDN :

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

82

Nói chung, yêu cầu "cách tốt nhất" mà không có bối cảnh cụ thể cũng giống như hỏi màu nào là tốt nhất ?

Một mặt, có nhiều màu sắc và không có màu nào đẹp nhất. Nó phụ thuộc vào nhu cầu và thường xuyên về hương vị, quá.

Mặt khác, có nhiều cách để lặp lại từ điển trong C # và không có cách nào tốt nhất. Nó phụ thuộc vào nhu cầu và thường xuyên về hương vị, quá.

Cách đơn giản nhất

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Nếu bạn chỉ cần giá trị (cho phép gọi nó item, dễ đọc hơn kvp.Value).

foreach (var item in items.Values)
{
    doStuff(item)
}

Nếu bạn cần một thứ tự sắp xếp cụ thể

Nói chung, người mới bắt đầu ngạc nhiên về thứ tự liệt kê của Từ điển.

LINQ cung cấp một cú pháp ngắn gọn cho phép chỉ định thứ tự (và nhiều thứ khác), ví dụ:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Một lần nữa bạn có thể chỉ cần giá trị. LINQ cũng cung cấp một giải pháp ngắn gọn để:

  • Lặp lại trực tiếp trên giá trị (cho phép gọi nó item, dễ đọc hơnkvp.Value )
  • nhưng được sắp xếp theo các phím

Đây là:

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

Có rất nhiều trường hợp sử dụng trong thế giới thực mà bạn có thể làm từ những ví dụ này. Nếu bạn không cần một đơn đặt hàng cụ thể, chỉ cần tuân theo "cách đơn giản nhất" (xem bên trên)!


Điều cuối cùng nên .Valuesvà không phải là một mệnh đề chọn.
Mafii

@Mafii Bạn có chắc không? Các giá trị được trả về bởi OrderBy không thuộc loại KeyValuePair, chúng không có Valuetrường. Loại chính xác tôi thấy ở đây là IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Có lẽ bạn có ý gì khác? Bạn có thể viết một dòng hoàn chỉnh cho thấy những gì bạn có ý nghĩa (và kiểm tra nó)?
Stéphane Gourichon

Tôi nghĩ rằng câu trả lời này chứa những gì tôi muốn nói: stackoverflow.com/a/141105/5962841 nhưng chính xác cho tôi nếu tôi nhầm lẫn một cái gì đó
Mafii

1
@Mafii Đọc lại toàn bộ câu trả lời của tôi, giải thích giữa các phần mã cho biết bối cảnh. Câu trả lời bạn đề cập giống như phần mã thứ hai trong câu trả lời của tôi (không yêu cầu đặt hàng). Ở đó tôi chỉ viết items.Valuenhư bạn đề nghị. Trong trường hợp của phần thứ tư mà bạn nhận xét, đây Select()là một cách gây ra foreachđể liệt kê trực tiếp các giá trị trong từ điển thay vì các cặp khóa-giá trị. Nếu bằng cách nào đó bạn không thích Select()trong trường hợp này, bạn có thể thích phần mã thứ ba. Điểm của phần thứ tư là chỉ ra rằng người ta có thể xử lý trước bộ sưu tập với LINQ.
Stéphane Gourichon

1
Nếu bạn .Keys.Orderby()sẽ lặp đi lặp lại trên một danh sách các khóa. Nếu đó là tất cả những gì bạn cần, tốt thôi. Nếu bạn cần các giá trị, thì trong vòng lặp bạn phải truy vấn từ điển trên mỗi khóa để lấy giá trị. Trong nhiều kịch bản, nó sẽ không tạo ra sự khác biệt thực tế. Trong kịch bản hiệu suất cao, nó sẽ. Giống như tôi đã viết ở đầu câu trả lời: "có nhiều cách (...) và không có cách nào tốt nhất. Nó phụ thuộc vào nhu cầu và thường là về hương vị."
Stéphane Gourichon

53

Tôi muốn nói foreach là cách tiêu chuẩn, mặc dù nó rõ ràng phụ thuộc vào những gì bạn đang tìm kiếm

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

Có phải đó là những gì bạn đang tìm kiếm?


42
Ừm, không đặt tên cho mục "giá trị" khá khó hiểu sao? Thông thường, bạn sẽ sử dụng cú pháp như "value.Key" và "value.Value", điều này không trực quan cho bất kỳ ai khác đang đọc mã, đặc biệt là nếu họ không quen với cách thực hiện .Net Dictionary. .
RenniePet

3
@RenniePet kvpthường được sử dụng để đặt tên cho các phiên bản KeyValuePair khi lặp lại từ điển và cấu trúc dữ liệu liên quan : foreach(var kvp in myDictionary){....
mbx

37

Bạn cũng có thể thử điều này trên các từ điển lớn để xử lý đa luồng.

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

8
@WiiMaxx và quan trọng hơn nếu các mục này KHÔNG phụ thuộc vào nhau
Mafii

36

C # 7.0 đã giới thiệu Deconstructor và nếu bạn đang sửdụng Ứng dụng .NET Core 2.0+ , cấu trúcKeyValuePair<>đã bao gồm mộtDeconstruct()cho bạn. Vì vậy, bạn có thể làm:

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

nhập mô tả hình ảnh ở đây


5
Nếu bạn sử dụng .NET Framework, ít nhất là tới 4.7.2 không có phần Giải mã trên KeyValuePair, hãy thử điều này:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith

29

Tôi đánh giá cao câu hỏi này đã có rất nhiều câu trả lời nhưng tôi muốn đưa vào một nghiên cứu nhỏ.

Lặp lại từ điển có thể khá chậm khi so sánh với lặp qua một cái gì đó như một mảng. Trong các thử nghiệm của tôi, một lần lặp trên một mảng mất 0,015003 giây trong khi một lần lặp trên một từ điển (có cùng số phần tử) mất 0,0365073 giây dài gấp 2,4 lần! Mặc dù tôi đã thấy sự khác biệt lớn hơn nhiều. Để so sánh, một Danh sách nằm ở khoảng giữa 0,00215043 giây.

Tuy nhiên, điều đó giống như so sánh táo và cam. Quan điểm của tôi là lặp đi lặp lại từ điển là chậm.

Từ điển được tối ưu hóa cho việc tra cứu, do đó, trong tâm trí tôi đã tạo ra hai phương pháp. Một người chỉ đơn giản là làm một bài giảng, người kia lặp lại các phím sau đó nhìn lên.

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

Cái này tải các khóa và lặp lại trên chúng thay vào đó (tôi cũng đã thử kéo các phím thành một chuỗi [] nhưng sự khác biệt là không đáng kể.

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

Với ví dụ này, bài kiểm tra foreach bình thường đã lấy 0,0310062 và phiên bản khóa mất 0,2205441. Tải tất cả các khóa và lặp đi lặp lại trên tất cả các tra cứu rõ ràng là rất chậm!

Đối với thử nghiệm cuối cùng, tôi đã thực hiện lặp đi lặp lại mười lần để xem liệu có bất kỳ lợi ích nào khi sử dụng các phím ở đây không (đến thời điểm này tôi chỉ tò mò):

Đây là phương pháp RunTest nếu điều đó giúp bạn hình dung những gì đang diễn ra.

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

Ở đây, lần chạy foreach bình thường mất 0,2820564 giây (lâu hơn khoảng mười lần so với một lần lặp duy nhất - như bạn mong đợi). Lặp lại các phím mất 2,2249449 giây.

Đã chỉnh sửa để thêm: Đọc một số câu trả lời khác khiến tôi đặt câu hỏi điều gì sẽ xảy ra nếu tôi sử dụng Từ điển thay vì Từ điển. Trong ví dụ này, mảng mất 0,0120024 giây, danh sách 0,015037 giây và từ điển 0,0465093 giây. Thật hợp lý khi hy vọng rằng kiểu dữ liệu tạo ra sự khác biệt về mức độ chậm hơn của từ điển.

Kết luận của tôi là gì?

  • Tránh lặp lại từ điển nếu bạn có thể, chúng chậm hơn đáng kể so với lặp qua một mảng có cùng dữ liệu trong đó.
  • Nếu bạn chọn lặp lại từ điển, đừng cố quá thông minh, mặc dù chậm hơn bạn có thể làm điều tồi tệ hơn nhiều so với sử dụng phương pháp foreach tiêu chuẩn.

11
Bạn nên đo bằng thứ gì đó như Đồng hồ bấm giờ thay vì DateTime: hanselman.com/blog/ cấp
Ngay cả Miên

2
bạn có thể vui lòng mô tả kịch bản thử nghiệm của mình không, có bao nhiêu mục trong từ điển của bạn, tần suất bạn chạy kịch bản của mình để tính thời gian trung bình, ...
WiiMaxx 29/07/2015

3
Thật thú vị, bạn sẽ nhận được kết quả khác nhau tùy thuộc vào dữ liệu bạn có trong từ điển. Trong khi lặp lại từ điển, hàm Enumerator phải bỏ qua rất nhiều vị trí trống trong từ điển, điều này làm cho nó chậm hơn so với việc lặp qua một mảng. Nếu từ điển đầy lên sẽ có ít chỗ trống để bỏ qua hơn nếu nó trống một nửa.
Martin Brown

26

Có rất nhiều lựa chọn. Yêu thích cá nhân của tôi là bởi KeyValuePair

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

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

Bạn cũng có thể sử dụng Bộ sưu tập Khóa và Giá trị


14

Với .NET Framework 4.7một người có thể sử dụng phân hủy

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

Để làm cho mã này hoạt động trên các phiên bản C # thấp hơn, hãy thêm System.ValueTuple NuGet packagevà viết ở đâu đó

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

9
Điều này là không chính xác. .NET 4.7 đơn giản đã ValueTupleđược tích hợp sẵn. Nó có sẵn dưới dạng gói nuget cho các phiên bản trước. Quan trọng hơn, C # 7.0+ là cần thiết để Deconstructphương thức hoạt động như một công cụ giải mã cho var (fruit, number) in fruits.
David Arno

13

Kể từ C # 7, bạn có thể giải cấu trúc các đối tượng thành các biến. Tôi tin rằng đây là cách tốt nhất để lặp lại từ điển.

Thí dụ:

Tạo một phương thức mở rộng trên KeyValuePair<TKey, TVal>đó giải mã nó:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

Lặp lại bất kỳ Dictionary<TKey, TVal>theo cách nào sau đây

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

10

Bạn đề nghị dưới đây để lặp đi lặp lại

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

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

FYI, foreachkhông hoạt động nếu giá trị thuộc loại đối tượng.


4
Xin hãy giải thích: foreachsẽ không hoạt động nếu giá trị là loại object? Nếu không thì điều này không có nhiều ý nghĩa.
Marc L.

9

Hình thức đơn giản nhất để lặp lại một từ điển:

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

9

Sử dụng C # 7 , thêm phương thức mở rộng này vào bất kỳ dự án nào của giải pháp của bạn:

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


Và sử dụng cú pháp đơn giản này

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


Hoặc cái này, nếu bạn thích

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


Thay thế cho truyền thống

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

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


Phương thức mở rộng biến đổi KeyValuePaircủa bạn IDictionary<TKey, TValue>thành một kiểu gõ mạnhtuple , cho phép bạn sử dụng cú pháp thoải mái mới này.

Nó chuyển đổi -just- các mục từ điển cần thiết thành tuples, vì vậy nó KHÔNG chuyển đổi toàn bộ từ điển thànhtuples , do đó không có mối quan tâm về hiệu suất liên quan đến điều đó.

Chỉ có một chi phí nhỏ khi gọi phương thức tiện ích mở rộng để tạo tupleso sánh với việc sử dụng KeyValuePairtrực tiếp, điều này KHÔNG phải là vấn đề nếu bạn chỉ địnhKeyValuePair thuộc tính KeyValue cho các biến vòng lặp mới.

Trong thực tế, cú pháp mới này rất phù hợp với hầu hết các trường hợp, ngoại trừ các kịch bản hiệu suất cực cao ở mức độ thấp, trong đó bạn vẫn có tùy chọn đơn giản là không sử dụng nó vào vị trí cụ thể đó.

Hãy xem điều này: Blog MSDN - Các tính năng mới trong C # 7


1
Điều gì sẽ là lý do để thích các bộ dữ liệu 'thoải mái' hơn cho cặp khóa-giá trị? Tôi không thấy lợi ích ở đây. Bộ dữ liệu của bạn chứa một khóa và một giá trị, cặp khóa-giá trị cũng vậy.
Maarten

4
Xin chào Maarten, cảm ơn câu hỏi của bạn. Lợi ích chính là khả năng đọc mã mà không cần nỗ lực lập trình bổ sung. Với KeyValuePair, người ta phải luôn sử dụng biểu mẫu kvp.Keykvp.Valueđể sử dụng khóa và giá trị tương ứng. Với các bộ dữ liệu, bạn có thể linh hoạt đặt tên khóa và giá trị theo ý muốn mà không cần sử dụng các khai báo biến tiếp theo bên trong khối foreach. Ví dụ, bạn có thể đặt tên khóa của mình là factoryNamevà giá trị là models, đặc biệt hữu ích khi bạn nhận được các vòng lặp lồng nhau (từ điển của từ điển): bảo trì mã dễ dàng hơn nhiều. Chỉ cần cung cấp cho nó một thử! ;-)
sɐunıɔ qɐp

7

Tôi biết đây là một câu hỏi rất cũ, nhưng tôi đã tạo ra một số phương pháp mở rộng có thể hữu ích:

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

Bằng cách này tôi có thể viết mã như thế này:

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

6

Đôi khi nếu bạn chỉ cần liệt kê các giá trị, hãy sử dụng bộ sưu tập giá trị của từ điển:

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

Báo cáo bởi bài đăng này trong đó nêu rõ đây là phương pháp nhanh nhất: http://alapiinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


+1 để mang lại hiệu suất. Thật vậy, việc lặp lại từ điển chính nó bao gồm một số chi phí nếu tất cả những gì bạn cần là giá trị. Điều đó chủ yếu là do sao chép các giá trị hoặc tham chiếu vào các cấu trúc KeyValuePair.
Jaanus Varus

1
FYI thử nghiệm cuối cùng trong liên kết đó là thử nghiệm sai: nó đo lặp lại các khóa, bỏ qua các giá trị, trong khi hai thử nghiệm trước đó sử dụng giá trị.
Lưỡi liềm tươi

5

Tôi đã tìm thấy phương thức này trong tài liệu cho lớp DictionaryBase trên MSDN:

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

Đây là người duy nhất tôi có thể hoạt động chính xác trong một lớp được kế thừa từ DictionaryBase.


3
Điều này trông giống như khi sử dụng phiên bản Từ điển không chung chung ... tức là trước .NET framework 2.0.
joedotnot

@ joed0tnot: đó là phiên bản không chung chung được sử dụng cho Hashtablecác đối tượng
sɐunıɔ qɐp

5

foreachlà nhanh nhất và nếu bạn chỉ lặp đi lặp lại ___.Values, nó cũng nhanh hơn

nhập mô tả hình ảnh ở đây


Tại sao bạn gọi ContainsKey()trong forphiên bản? Điều đó bổ sung thêm chi phí không có trong mã mà bạn đang so sánh. TryGetValue()tồn tại để thay thế chính xác mẫu "nếu khóa tồn tại, lấy vật phẩm bằng khóa". Hơn nữa, nếu dictchứa một phạm vi số nguyên liền kề từ 0đến dictCount - 1, bạn biết rằng bộ chỉ mục không thể thất bại; nếu không, dict.Keyslà những gì bạn nên lặp đi lặp lại. Dù bằng cách nào, không ContainsKey()/ TryGetValue()cần thiết. Cuối cùng, xin vui lòng không đăng ảnh chụp màn hình của mã.
BACON

3

Tôi sẽ tận dụng lợi thế của .NET 4.0+ và cung cấp câu trả lời cập nhật cho câu trả lời ban đầu được chấp nhận:

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

3

Cách tiêu chuẩn để lặp lại từ điển, theo tài liệu chính thức về MSDN là:

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

2

Tôi đã viết một phần mở rộng để lặp qua một từ điển.

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

Sau đó bạn có thể gọi

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

Bạn đã xác định một ForEachphương thức mà bạn có foreach (...) { }... Có vẻ như không cần thiết.
Maarten

1

Nếu nói, bạn muốn lặp lại bộ sưu tập giá trị theo mặc định, tôi tin rằng bạn có thể triển khai IEnumerable <>, trong đó T là loại đối tượng giá trị trong từ điển và "đây" là Từ điển.

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

1

Chỉ muốn thêm 2 xu của tôi, vì hầu hết các câu trả lời liên quan đến vòng lặp foreach. Xin vui lòng, hãy xem mã sau đây:

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

Đã đọc điều này thêm một lệnh gọi '.ToList ()', có thể có một sự cải thiện hiệu suất nhỏ (như được chỉ ra ở đây foreach vs someList.Foreach () {} ), đặc biệt khi làm việc với Từ điển lớn và chạy song song là không tùy chọn / sẽ không có hiệu lực nào cả.

Ngoài ra, xin lưu ý rằng bạn sẽ không thể gán giá trị cho thuộc tính 'Giá trị' bên trong vòng lặp foreach. Mặt khác, bạn cũng có thể điều khiển 'Khóa', có thể khiến bạn gặp rắc rối khi chạy.

Khi bạn chỉ muốn "đọc" Khóa và Giá trị, bạn cũng có thể sử dụng IEnumerable.Select ().

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

5
Sao chép toàn bộ bộ sưu tập mà không có lý do nào cả sẽ không cải thiện hiệu suất. Nó sẽ làm chậm đáng kể mã, cũng như tăng gấp đôi dung lượng bộ nhớ của mã phải tiêu thụ hầu như không có bộ nhớ bổ sung.
Phục vụ

Tôi tránh phương thức 'List.ForEach' của hiệu ứng phụ: foreachbuộc khả năng hiển thị hiệu ứng phụ lên, nơi nó thuộc về.
dùng2864740

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

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

2
Cần phải có nhiều biện minh / mô tả trong câu trả lời này. Không AggregateObjectthêm KeyValuePairgì? Trường hợp "lặp lại", như được yêu cầu trong câu hỏi?
Marc L.

Chọn lặp trên từ điển và cho phép chúng tôi làm việc trên từng đối tượng. Nó không chung chung như foreach, nhưng tôi đã sử dụng nó rất nhiều. Có phải câu trả lời của tôi thực sự xứng đáng downvote?
Pixar

Không, Select sử dụng phép lặp để tạo kết quả, nhưng bản thân nó không phải là trình lặp. Các loại điều mà phép lặp ( foreach) được sử dụng cho - đặc biệt là các hoạt động có tác dụng phụ - nằm ngoài phạm vi của Linq, bao gồm Select. Lambda sẽ không chạy cho đến khi aggregateObjectCollectionthực sự được liệt kê. Nếu câu trả lời này được coi là "con đường đầu tiên" (nghĩa là được sử dụng trước một đường thẳng foreach) thì nó khuyến khích các thực hành xấu. Về mặt tình huống, có thể các hoạt động Linq hữu ích trước khi lặp lại từ điển, nhưng điều đó không giải quyết được câu hỏi như đã hỏi.
Marc L.

0

Từ điển <TKey, TValue> Đây là lớp tập hợp chung trong c # và nó lưu trữ dữ liệu ở định dạng giá trị khóa. Khóa phải là duy nhất và không thể là null trong khi giá trị có thể trùng lặp và null. Mỗi mục trong từ điển là được coi là cấu trúc KeyValuePair <TKey, TValue> đại diện cho một khóa và giá trị của nó. và do đó chúng ta nên lấy loại phần tử KeyValuePair <TKey, TValue> trong quá trình lặp lại của phần tử. Dưới đây là ví dụ.

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

0

Nếu bạn muốn sử dụng cho vòng lặp, bạn có thể làm điều này:

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

Lợi ích của việc này là gì? Đó là mã dài hơn một foreachvòng lặp hiệu suất kém hơn bởi vì new List<string>(dictionary.Keys)sẽ lặp lại dictionary.Countnhiều lần trước khi bạn thậm chí có cơ hội lặp lại chính nó. Đặt sang một bên rằng yêu cầu "cách tốt nhất" là chủ quan, tôi không thấy cách này sẽ đủ điều kiện là "cách tốt nhất" hoặc "cách tiêu chuẩn" mà câu hỏi tìm kiếm. Để "Nếu bạn muốn sử dụng cho vòng lặp ..." Tôi sẽ truy cập bằng " Đừng sử dụng forvòng lặp."
BACON

Nếu bạn có bộ sưu tập lớn và các thao tác chậm trong foreach và nếu bộ sưu tập của bạn có thể thay đổi khi bạn lặp lại, nó sẽ bảo vệ bạn khỏi lỗi "bộ sưu tập đã thay đổi" và giải pháp này có hiệu suất tốt hơn khi sử dụng ElementAt.
Seçkin Durgay

Tôi đồng ý rằng việc tránh các trường hợp ngoại lệ "Bộ sưu tập đã được sửa đổi" là một lý do để làm điều này, mặc dù trường hợp đặc biệt đó không được nêu trong câu hỏi và người ta luôn có thể làm foreach (var pair in dictionary.ToArray()) { }. Tuy nhiên, tôi nghĩ sẽ tốt hơn nếu làm rõ trong câu trả lời (các) kịch bản cụ thể mà người ta muốn sử dụng mã này và ý nghĩa của việc làm như vậy.
BACON

vâng bạn đúng nhưng có một câu trả lời ở đây với ElementAt và nó có uy tín rất cao và tôi đã nhập câu trả lời này :)
Seçkin Durgay

0

Như đã chỉ ra trong câu trả lời này , KeyValuePair<TKey, TValue>thực hiện một Deconstructphương thức bắt đầu trên .NET Core 2.0, .NET Standard 2.1 và .NET Framework 5.0 (xem trước).

Với điều này, có thể lặp lại từ điển KeyValuePairtheo cách bất khả tri:

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

// ...

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

-1

đơn giản với 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}") );

Tôi thấy rằng bạn đang gọi ToList()bởi vì ForEach()chỉ được xác định trên List<>lớp, nhưng tại sao làm tất cả điều đó thay vì chỉ foreach (var pair in dict) { }? Tôi muốn nói rằng điều đó thậm chí còn đơn giản hơn và không có cùng hàm ý về bộ nhớ / hiệu năng. Giải pháp chính xác này đã được đề xuất trong câu trả lời này từ 3,5 năm trước, dù sao đi nữa.
BACON

Chỉ vì mục đích trực quan hóa mã, nó thực sự có thể được thực hiện theo cách bạn đã chỉ ra, tôi chỉ muốn trình bày nó theo một cách khác và theo quan điểm của tôi, tôi xin lỗi vì đã không thấy câu trả lời được chỉ ra, một cái ôm
Luiz Fernando Corrêa Leite

-3

ngoài các bài đăng xếp hạng cao nhất, nơi có một cuộc thảo luận giữa việc sử dụng

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

hoặc là

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

Hoàn thiện nhất là như sau vì bạn có thể thấy loại từ điển từ khi khởi tạo, kvp là 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
}

5
Tạo và sao chép một từ điển thứ hai không phải là một giải pháp hợp lệ cho khả năng đọc mã. Trong thực tế, tôi sẽ lập luận rằng nó sẽ làm cho mã khó hiểu hơn bởi vì bây giờ bạn phải tự hỏi: "Tại sao người cuối cùng tạo ra một từ điển thứ hai?" Nếu bạn muốn dài dòng hơn, chỉ cần sử dụng tùy chọn một.
Matthew Goulart

Tôi chỉ có ý cho bạn thấy rằng khi từ chối dict trước cho mỗi, việc sử dụng trong foreach là rõ ràng từ tuyên bố
BigChief
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.