Widziałem kilka różnych sposobów iteracji słownika w języku C #. Czy istnieje standardowy sposób?
c#
dictionary
loops
Jake Stewart
źródło
źródło
Odpowiedzi:
źródło
var entry
W takim przypadku użycie jest lepsze, dlatego głosowałem na tę odpowiedź po raz drugi zamiast powyższego.var
gdy nie wiesz, że typ jest ogólnie złą praktyką.var
działa tylko wtedy, gdy typ jest znany w czasie kompilacji. Jeśli program Visual Studio zna ten typ, możesz go również dowiedzieć.Jeśli próbujesz użyć ogólnego słownika w języku C #, tak jakbyś użył tablicy asocjacyjnej w innym języku:
Lub, jeśli potrzebujesz tylko iteracji po kolekcji kluczy, użyj
I na koniec, jeśli interesują Cię tylko wartości:
(Pamiętaj, że
var
słowo kluczowe jest opcjonalną funkcją C # 3.0 i nowszą, tutaj możesz również użyć dokładnego rodzaju swoich kluczy / wartości)źródło
myDictionary
(chyba że jest to oczywiście nazwa). Myślę, że używanie var jest dobre, gdy typ jest oczywisty, np.var x = "some string"
Ale gdy nie jest to od razu oczywiste, myślę, że to leniwe kodowanie, które boli czytnika / recenzenta koduvar
moim zdaniem powinien być stosowany oszczędnie. Szczególnie w tym przypadku nie jest konstruktywny: typKeyValuePair
jest prawdopodobnie istotny dla pytania.var
ma wyjątkowy cel i nie sądzę, że jest to cukier „syntaktyczny”. Używanie go celowo jest właściwym podejściem.W niektórych przypadkach może być potrzebny licznik, który może być zapewniony przez implementację pętli For. W tym celu LINQ zapewnia,
ElementAt
co umożliwia:źródło
ElementAt
jest operacja O (n)?.ElementAt
w tym kontekście może prowadzić do subtelnych błędów. O wiele poważniejszy jest punkt Arturo powyżej. Będziesz iterowałdictionary.Count + 1
czasy słownikowe prowadzące do złożoności O (n ^ 2) dla operacji, która powinna być tylko O (n). Jeśli naprawdę potrzebujesz indeksu (jeśli tak, to prawdopodobnie używasz niewłaściwego typu kolekcji), powinieneś iterowaćdictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
i nie używać.ElementAt
wewnątrz pętli.Zależy, czy szukasz kluczy, czy wartości ...
Z
Dictionary(TKey, TValue)
opisu klasy MSDN :źródło
Ogólnie rzecz biorąc, pytanie o „najlepszy sposób” bez określonego kontekstu jest jak pytanie o najlepszy kolor ?
Z jednej strony istnieje wiele kolorów i nie ma najlepszego koloru. To zależy od potrzeby, a często także od smaku.
Z drugiej strony istnieje wiele sposobów na iterację w Słowniku w języku C # i nie ma najlepszego sposobu. To zależy od potrzeby, a często także od smaku.
Najprostszy sposób
Jeśli potrzebujesz tylko wartości (pozwala to nazwać
item
, bardziej czytelna niżkvp.Value
).Jeśli potrzebujesz określonej kolejności sortowania
Zasadniczo początkujący są zaskoczeni kolejnością wyliczania słownika.
LINQ zapewnia zwięzłą składnię, która pozwala określić kolejność (i wiele innych rzeczy), np .:
Ponownie możesz potrzebować tylko tej wartości. LINQ zapewnia również zwięzłe rozwiązanie dla:
item
, bardziej czytelną niżkvp.Value
)Oto on:
Z tych przykładów można zrobić wiele innych rzeczywistych przypadków użycia. Jeśli nie potrzebujesz konkretnego zamówienia, po prostu trzymaj się „najprostszej drogi” (patrz wyżej)!
źródło
.Values
a nie klauzulą select.Value
pola. Dokładny typ, który tu widzę, toIOrderedEnumerable<KeyValuePair<TKey, TValue>>
. Może miałeś na myśli coś innego? Czy potrafisz napisać pełną linię, która ma na myśli (i przetestować)?items.Value
jak zasugerowałeś. W przypadku czwartej sekcji, którą skomentowałeś,Select()
jest to sposóbforeach
na wyliczenie bezpośrednio wartości w słowniku zamiast par klucz-wartość. Jeśli jakoś ci się nie podobaSelect()
w tym przypadku, możesz wybrać trzecią sekcję kodu. Celem czwartej sekcji jest pokazanie, że można wstępnie przetworzyć kolekcję za pomocą LINQ..Keys.Orderby()
, przejdziesz do listy kluczy. Jeśli to wszystko, czego potrzebujesz, w porządku. Jeśli potrzebujesz wartości, w pętli będziesz musiał sprawdzić słownik każdego klucza, aby uzyskać wartość. W wielu scenariuszach nie będzie to miało praktycznej różnicy. W scenariuszu o wysokiej wydajności tak będzie. Tak jak napisałem na początku odpowiedzi: „istnieje wiele sposobów (...) i nie ma najlepszego sposobu. Zależy to od potrzeby, a często także od smaku”.Powiedziałbym, że foreach jest standardowym sposobem, choć oczywiście zależy od tego, czego szukasz
Czy tego szukasz?
źródło
kvp
jest powszechnie stosowany do nazwy instancji KeyValuePair podczas iteracji nad słownikami i związanych z nimi struktur danych:foreach(var kvp in myDictionary){...
.Możesz także wypróbować to w dużych słownikach do przetwarzania wielowątkowego.
źródło
W wersji C # 7.0 wprowadzono dekonstruktory, a jeśli używasz aplikacji .NET Core 2.0+ , struktura
KeyValuePair<>
już zawieraDeconstruct()
dla ciebie. Możesz więc zrobić:źródło
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
Rozumiem, że na to pytanie udzielono już wielu odpowiedzi, ale chciałem przeprowadzić trochę badań.
Iterowanie po słowniku może być dość powolne w porównaniu z iteracją po czymś takim jak tablica. W moich testach iteracja na tablicy zajęła 0,015003 sekundy, podczas gdy iteracja na słowniku (z taką samą liczbą elementów) zajęła 0,0365073 sekundy, czyli 2,4 razy dłużej! Chociaż widziałem znacznie większe różnice. Dla porównania lista była gdzieś pomiędzy 0,00215043 sekund.
To jednak jak porównywanie jabłek i pomarańczy. Chodzi mi o to, że iteracja po słownikach jest powolna.
Słowniki są zoptymalizowane do wyszukiwania, więc mając to na uwadze, stworzyłem dwie metody. Jeden po prostu robi foreach, drugi iteruje klucze, a potem podnosi wzrok.
Ten ładuje klucze i zamiast tego iteruje je (próbowałem też wciągnąć klucze do łańcucha [], ale różnica była znikoma.
W tym przykładzie normalny test foreach trwał 0,0310062, a wersja kluczy - 0,2205441. Ładowanie wszystkich kluczy i powtarzanie wszystkich wyszukiwań jest zdecydowanie DUŻO wolniejsze!
W ostatnim teście wykonałem iterację dziesięć razy, aby sprawdzić, czy korzystanie z klawiszy jest w tym przypadku korzystne (w tym momencie byłem po prostu ciekawy):
Oto metoda RunTest, jeśli pomaga to zobrazować, co się dzieje.
Tutaj normalny przebieg Foreach trwał 0,2820564 sekund (około dziesięć razy dłużej niż pojedyncza iteracja - jak można się spodziewać). Iteracja klawiszy zajęła 2.2249449 sekund.
Edytowane w celu dodania: przeczytanie niektórych innych odpowiedzi skłoniło mnie do pytania, co by się stało, gdybym użył Słownika zamiast Słownika. W tym przykładzie tablica zajęła 0,0120024 sekundy, lista 0,0185037 sekund i słownik 0,0465093 sekundy. Można oczekiwać, że typ danych ma wpływ na to, o ile wolniej działa słownik.
Jakie są moje wnioski ?
źródło
Istnieje wiele opcji. Moim osobistym faworytem jest KeyValuePair
Możesz także użyć kolekcji kluczy i wartości
źródło
Z
.NET Framework 4.7
jednym można użyć rozkładuAby ten kod działał w niższych wersjach C #, dodaj
System.ValueTuple NuGet package
i napisz gdzieśźródło
ValueTuple
wbudowane. Jest dostępny jako pakiet nuget dla wcześniejszych wersji. Co ważniejsze, C # 7.0+ jest potrzebny, abyDeconstruct
metoda działała jako dekodervar (fruit, number) in fruits
.Począwszy od C # 7, można dekonstruować obiekty na zmienne. Uważam, że jest to najlepszy sposób na iterację słownika.
Przykład:
Utwórz metodę rozszerzenia,
KeyValuePair<TKey, TVal>
która go dekonstruuje:Powtarzaj dowolne
Dictionary<TKey, TVal>
w następujący sposóbźródło
Sugerujesz poniżej, aby iterować
Do Twojej wiadomości,
foreach
nie działa, jeśli wartość jest typu obiekt.źródło
foreach
nie będzie działać, jeśli która wartość jest typuobject
? W przeciwnym razie nie ma to większego sensu.Najprostsza forma do iteracji słownika:
źródło
Korzystając z C # 7 , dodaj tę metodę rozszerzenia do dowolnego projektu swojego rozwiązania:
I użyj tej prostej składni
Lub ten, jeśli wolisz
W miejsce tradycyjnego
Metoda rozszerzenia przekształca
KeyValuePair
twojąIDictionary<TKey, TValue>
w silnie typowanątuple
, umożliwiając ci użycie tej nowej wygodnej składni.Konwertuje -just- wymagane pozycje słownika na
tuples
, więc NIE konwertuje całego słownika natuples
, więc nie ma z tym żadnych problemów związanych z wydajnością.Istnieje tylko niewielki koszt wywołania metody rozszerzenia dla utworzenia
tuple
w porównaniu zKeyValuePair
bezpośrednim użyciem , co NIE powinno stanowić problemu, jeśli przypisujeszKeyValuePair
właściwościKey
iValue
nowe zmienne pętli.W praktyce ta nowa składnia bardzo dobrze pasuje do większości przypadków, z wyjątkiem scenariuszy o bardzo wysokiej wydajności na niskim poziomie, w których nadal możesz po prostu nie używać jej w tym konkretnym miejscu.
Sprawdź to: Blog MSDN - Nowe funkcje w C # 7
źródło
kvp.Key
ikvp.Value
do używania odpowiednio klucza i wartości. Dzięki krotkom możesz dowolnie nazywać klucz i wartość, bez potrzeby używania dalszych deklaracji zmiennych w bloku foreach. Na przykład możesz nazwać swój klucz jakofactoryName
, a wartość jakomodels
, co jest szczególnie przydatne, gdy otrzymujesz zagnieżdżone pętle (słowniki słowników): obsługa kodu staje się znacznie łatwiejsza. Po prostu daj temu szansę! ;-)Wiem, że to bardzo stare pytanie, ale stworzyłem kilka metod rozszerzenia, które mogą być przydatne:
W ten sposób mogę napisać taki kod:
źródło
Czasami, jeśli potrzebujesz tylko wyliczyć wartości, skorzystaj z kolekcji wartości słownika:
Zgłoszone przez ten post, który stwierdza, że jest to najszybsza metoda: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html
źródło
Znalazłem tę metodę w dokumentacji klasy DictionaryBase na MSDN:
To był jedyny, w którym mogłem poprawnie funkcjonować w klasie odziedziczonej ze DictionaryBase.
źródło
Hashtable
foreach
jest najszybszy, a jeśli tylko wykonasz iterację___.Values
, będzie także szybszyźródło
ContainsKey()
wfor
wersji? To dodaje dodatkowy narzut, którego nie ma w kodzie, z którym porównujesz.TryGetValue()
istnieje, aby zastąpić dokładny wzór „jeśli klucz istnieje, zdobądź przedmiot z kluczem”. Ponadto, jeślidict
zawiera ciągły zakres liczb całkowitych od0
dodictCount - 1
, wiesz, że indeksator nie może zawieść; w przeciwnymdict.Keys
razie powinieneś powtarzać. Tak czy inaczej, nieContainsKey()
/TryGetValue()
potrzebne. Na koniec nie publikuj zrzutów ekranu kodu.Wykorzystam .NET 4.0+ i przedstawię zaktualizowaną odpowiedź na pierwotnie zaakceptowaną:
źródło
Standardowy sposób iteracji w Słowniku, zgodnie z oficjalną dokumentacją MSDN, to:
źródło
Napisałem rozszerzenie do pętli w słowniku.
Następnie możesz zadzwonić
źródło
ForEach
metodę, w której maszforeach (...) { }
... Wydaje się niepotrzebny.Jeśli powiedzmy, że chcesz domyślnie iterować kolekcję wartości, uważam, że możesz zaimplementować IEnumerable <>, gdzie T to typ obiektu wartości w słowniku, a „this” to Dictionary.
źródło
Chciałem tylko dodać moje 2 centy, ponieważ większość odpowiedzi dotyczy pętli foreach. Proszę spojrzeć na następujący kod:
Mimo że dodaje to dodatkowe wywołanie funkcji „.ToList ()”, może wystąpić nieznaczna poprawa wydajności (jak wskazano tutaj foreach vs someList.Foreach () {} ), szczególnie w przypadku pracy z dużymi słownikami i równoległego działania nie opcja / nie przyniesie żadnego efektu.
Należy również pamiętać, że nie będzie można przypisać wartości do właściwości „Wartość” w pętli foreach. Z drugiej strony będziesz mógł również manipulować „kluczem”, co może spowodować kłopoty w czasie wykonywania.
Gdy chcesz po prostu „odczytać” klucze i wartości, możesz także użyć IEnumerable.Select ().
źródło
foreach
wymusza widoczność efektu ubocznego tam, gdzie należy.źródło
AggregateObject
dodajeKeyValuePair
? Gdzie jest „iteracja”, zgodnie z pytaniem w pytaniu?foreach
, ale często go używałem. Czy moja odpowiedź naprawdę zasługiwała na głos?Select
używa iteracji do uzyskania wyniku, ale nie jest samą iteratorem. Rodzaje rzeczy, do którychforeach
używana jest iteracja ( ) - zwłaszcza operacje z efektami ubocznymi - nie wchodzą w zakres Linq, w tymSelect
. Lambda nie uruchomi się, dopóki nieaggregateObjectCollection
zostanie wyliczona. Jeśli ta odpowiedź jest traktowana jako „pierwsza ścieżka” (tj. Stosowana przed stritemforeach
), zachęca do złych praktyk. W niektórych sytuacjach operacje Linq mogą być pomocne przed iteracją słownika, ale nie rozwiązują zadanego pytania.Słownik <TKey, TValue> Jest to ogólna klasa kolekcji w języku c # i przechowuje dane w formacie wartości klucza. Klucz musi być unikalny i nie może mieć wartości zerowej, natomiast wartość może być zduplikowana i pusta. Każda pozycja w słowniku jest traktowane jako struktura KeyValuePair <TKey, TValue> reprezentująca klucz i jego wartość. i dlatego powinniśmy wziąć typ elementu KeyValuePair <TKey, TValue> podczas iteracji elementu. Poniżej znajduje się przykład.
źródło
Jeśli chcesz użyć pętli, możesz to zrobić:
źródło
foreach
pętla i gorsza wydajność, ponieważnew List<string>(dictionary.Keys)
będą iterowaćdictionary.Count
czasy, zanim jeszcze będziesz mieć szansę na iterację. Pomijając fakt, że pytanie o „najlepszy sposób” jest subiektywne, nie rozumiem, jak można by to zakwalifikować jako „najlepszy sposób” lub „standardowy sposób”, którego szuka pytanie. Do „Jeśli chcesz użyć pętli ...” odpowiedziałbym „ Nie używajfor
pętli”.foreach (var pair in dictionary.ToArray()) { }
. Mimo to myślę, że dobrze byłoby wyjaśnić w odpowiedzi konkretny scenariusz (scenariusze), w których chciałoby się użyć tego kodu oraz konsekwencje takiego postępowania.proste z linq
źródło
ToList()
ponieważForEach()
jest zdefiniowany tylko wList<>
klasie, ale dlaczego to wszystko zamiast po prostuforeach (var pair in dict) { }
? Powiedziałbym, że jest to jeszcze prostsze i nie ma takich samych implikacji pamięci / wydajności. W każdym razie dokładnie to rozwiązanie zostało już zaproponowane w tej odpowiedzi sprzed 3,5 roku.oprócz postów o najwyższym rankingu, w których istnieje dyskusja między używaniem
lub
najbardziej kompletny jest następujący, ponieważ można zobaczyć typ słownika z inicjalizacji, kvp to KeyValuePair
źródło