Słownik kontra lista

30

Więc wpadłem na Dictionary<int, int>dzisiaj w pracy. Wydawało mi się to dziwne, ponieważ prawdopodobnie użyłbym List<int>zamiast tego. Czy jest jakaś różnica i czy byłby przypadek użycia, w którym jedna struktura byłaby preferowana względem drugiej?

ZeroDivide
źródło
1
Czy musi istnieć relacja między dwoma (lub więcej) danymi ints? Wtedy mapa (słownik w tym języku) ma sens.
Rig
3
Słownik nazw to dla mnie oczywiste. Gdy potrzebujesz czegoś szybko odszukać, korzystasz ze słownika.
ChaosPandion,
2
@ChaosPandion: List<T>w ramach .NET jest tablica o dostępie swobodnym, w której operacja wyszukiwania jest zwykle szybsza niż w przypadku Dictionary<int,T>.
Doc Brown
2
@DocBrown - Tylko w dość dziwnym przypadku użycia indeksu numerycznego jako klucza. W przeciwnym razie wyszukiwanie będzie szybsze podczas korzystania Dictionary<TKey, TValue>.
ChaosPandion,
2
@chaos to pytanie dotyczy tej dziwnej sprawy.
MarkJ

Odpowiedzi:

32

Użyłbyś a, Dictionary<int, int>jeśli twoje indeksy mają specjalne znaczenie oprócz samego umiejscowienia pozycyjnego.

Bezpośrednim przykładem, który przychodzi mi na myśl, jest przechowywanie kolumny identyfikatora i kolumny int w bazie danych. Na przykład, jeśli masz [person-id]kolumnę i [personal-pin]kolumnę, możesz umieścić je w Dictionary<int, int>. W ten sposób pinDict[person-id]otrzymasz kod PIN, ale indeks jest znaczący, a nie tylko pozycja w List<int>.

Ale tak naprawdę, za każdym razem, gdy masz dwie powiązane listy liczb całkowitych, może to być odpowiednia struktura danych.

Kris Harper
źródło
Jeśli mój identyfikator osoby należy do zakresu 0, ..., 999, i musiałbym załadować wartości osobistego kodu PIN do pamięci dla wszystkich 1000 osób, zwykle wybrałbym List<int>słownik, a nie słownik. Zobacz moją odpowiedź poniżej.
Doc Brown
3
tak, ale słownik może być rzadki
jk.
@jk: właśnie to starałem się rozwinąć w mojej odpowiedzi.
Doc Brown
7
Osobisty PIN? Brzmi trochę niepotrzebnie.
Jack
Hm, gdy indeks ma „specjalne znaczenie”, w rzeczywistych scenariuszach świata może być prawdopodobne, że nie tworzą one ciągłego zakresu [0, ..., n] (choć nie jest to obowiązkowe), więc ta odpowiedź brzmi nie po prostu źle, ale nieprecyzyjnie. Niemniej jednak decyzja IMHO nie powinna opierać się na tej „specjalnej rzeczy znaczenia”, ale tylko na „czy klucze budują w przybliżeniu interwał [0, ..., n]”. Sądzę, że na podstawie liczby głosów pozytywnych większość czytelników nie zauważyła tego punktu.
Dok. Brown
28

Pomyśl Listo tablicy Dictionaryjak o tablicy mieszającej . Używałbyś tylko Dictionary, gdybyś musiał mapować (lub kojarzyć) znaczące klucze do wartości, podczas gdy Listtylko mapował (lub kojarzył) pozycje (lub indeksy) z wartościami.

Powiedzmy na przykład, że chcesz zachować związek między wiekiem osoby a jej wzrostem. Możesz użyć a, Dictionary<int, int>aby zmapować wiek (an int) osoby na jej wzrost (an int):

Dictionary<int, int> personHeightMap = new Dictionary<int, int>();

personHeightMap.Add(21, 185);
personHeightMap.Add(31, 174);

int height = personHeightMap.ContainsKey(21) ? personHeightMap[21] : -1;

Niezbyt przydatny przykład, ale chodzi o to, że nie byłbyś w stanie zrobić tego tak elegancko, Listponieważ musiałby przechowywać te wartości na miejscu.

Bernard
źródło
7
+1 za wspomnienie, że Listdotyczy zamówienia , a Dictionarydotyczy skojarzenia . Jeśli potrzebujesz za każdym razem uzyskać dane w określonej kolejności lub ich kolejność względem siebie jest ważna, to Listjest droga. Dictionariesbywają nieuporządkowane i zajmują się mapowaniem relacji klucz -> wartość.
KChaloux
2
Na koniec, gdy wiesz, czego szukasz, tablica skrótów znajduje się w przybliżeniu w czasie O (1), podczas gdy tablica to O (logN) w najlepszym przypadku (posortowane i bez duplikatów) oraz O (N) w najgorszy przypadek.
JensG
1
+1. Wydaje się, że nikt inny nie zajął się kwestią, że listy są uporządkowane semantycznie, a dyktaty są przeglądami semantycznymi, co moim zdaniem jest absolutnie podstawowe .
Benjamin Hodgson,
15

Semantycznie, Dictionary<int, T>ai List<T>są bardzo podobne, oba są kontenerami o swobodnym dostępie w środowisku .NET. Aby użyć listy jako zamiennika słownika, potrzebujesz specjalnej wartości w swoim typie T(np. null) Do reprezentowania pustych miejsc na liście. Jeśli Tnie jest typu zerowalnego int, możesz użyć int?zamiast tego, lub jeśli tylko chcesz przechowywać wartości dodatnie, możesz również użyć specjalnej wartości, takiej jak -1, aby reprezentować puste miejsca.

Który wybierzesz, powinien zależeć od zakresu kluczowych wartości. Jeśli twoje klucze Dictionary<int, T>znajdują się w przedziale całkowitym, bez wielu przerw między nimi (na przykład 80 wartości z [0, ... 100]), to a List<T>będzie bardziej odpowiednie, ponieważ dostęp do indeksu jest szybszy i w tym przypadku jest mniej pamięci i czasu.

Jeśli twoje kluczowe wartości to 100 intwartości z zakresu takiego jak [0, ..., 1000000], to List<T>potrzebna jest pamięć do przechowywania 1000000 wartości T, gdzie słownik będzie potrzebował pamięci rzędu wielkości około 100 wartości T, 100 wartości int (plus pewien narzut, w rzeczywistości oczekujemy około 2 razy więcej pamięci do przechowywania tych 100 kluczy i wartości). W tym drugim przypadku słownik będzie bardziej odpowiedni.

Doktor Brown
źródło
6
to jest ważna różnica imho, Dictionary <int, int> może być rzadki
jk.
W takim przypadku nie możemy użyć List <KeyValuePair <int, int >>? Który z nich byłby lepszy dla przejścia liniowego?
Deepak Mishra
@DeepakMishra: główna różnica polega na tym, List<KeyValuePair<int,T>>że nie jest dostępna operacja wyszukiwania O (1). Po drugie, elementy List<KeyValuePair<int,T>>mogą mieć określoną kolejność, niezależną od ich kluczowych wartości. Jeśli potrzebujesz tego drugiego, ale nie pierwszego, List<KeyValuePair<int,T>>lub List<Tuple<int,T>>może być lepszym wyborem. Jeśli potrzebujesz obu, jest również OrderedDictionary.
Doc Brown
@DocBrown Który z nich byłby lepszy w przypadku ruchu liniowego (tj. Foreach) i operacji wstawiania, nie ma potrzeby bezpośredniego wyszukiwania?
Deepak Mishra
@DeepakMishra: w tworzeniu oprogramowania nie ma czegoś takiego jak „ogólnie lepiej”. Lepsze tutaj może oznaczać szybsze, lepsze czytanie, mniej kodu do pisania, łatwiejsze rozszerzanie na nadchodzące wymagania. Ale ogólnie rzecz biorąc, przestańmy nad tym zastanawiać, wdrożyć taki, który rozwiązuje problem pod ręką poprawnie i jest najprostszy w oczach , sprawdzić, czy jest wystarczająco szybki dla twojego celu i zainwestować w niego tylko więcej myśli, gdy zauważysz wady.
Doc Brown
6

Jak ktokolwiek może uznać je za równoważne?

Słownik jest rzadki i pozwala na losowe wstawianie, ale sprawia, że ​​przechodzenie w kolejności jest problemem, lista nie jest rzadka, a wstawianie poza kolejnością jest drogie, z natury zapewnia przechodzenie w kolejności.

Byłoby bardzo niewiele sytuacji, w których jedna nie byłaby dramatycznie lepsza od drugiej.

Loren Pechtel
źródło
2

Poza tym: inne języki programowania określają ten typ struktury danych jako mapę, a nie słownik.

Jeśli twoje dane można w znaczący sposób zdefiniować jako pary klucz / wartość, Słownik zapewni znacznie szybszy dostęp, jeśli będziesz musiał znaleźć wartość za pomocą jej klucza.

Załóżmy na przykład, że masz listę klientów. Każdy klient zawiera takie dane, jak nazwa i adres oraz unikalny numer klienta. Załóżmy, że masz także listę przetwarzanych zamówień. Każde zamówienie będzie zawierało szczegóły tego, co jest robione, i będzie musiało zawierać numer klienta osoby, która je zamówiła.

Kiedy zamówienie jest gotowe do wysyłki, musisz znaleźć adres, na który chcesz je wysłać. Jeśli klienci są zapisani jako zwykła lista, musisz przeszukać całą listę, aby znaleźć klienta z odpowiednim numerem klienta. Zamiast tego możesz przechowywać klientów w Słowniku, podając numer klienta jako klucz. Słownik pozwoli teraz wyciągnąć właściwego klienta w jednym kroku, bez konieczności wyszukiwania.

Simon B.
źródło
1

Słownik używa haszowania do wyszukiwania danych. Słownik najpierw obliczył wartość skrótu dla klucza i ta wartość skrótu prowadzi do docelowego segmentu danych. Następnie każdy element w wiadrze należy sprawdzić pod kątem równości. Ale tak naprawdę lista będzie szybsza niż słownik przy wyszukiwaniu pierwszego elementu, ponieważ nic nie trzeba przeszukiwać w pierwszym kroku. Ale w drugim kroku lista musi przejrzeć pierwszy element, a następnie drugi element. Dlatego każdy krok zajmuje coraz więcej czasu. Im większa lista, tym dłużej trwa.

Więcej o .... Słownik Vs Lista z przykładem.

Walshregal
źródło
-1

Jeśli dany kod przechowuje dwa zestawy skorelowanych wartości, klasa Dictionary zapewnia indeksowany sposób wyszukiwania wartości według klucza. Jeśli istnieje tylko jeden zestaw wartości, ale do tego zestawu należy uzyskać dostęp losowo (być może w celu sprawdzenia istnienia klucza w zestawie), a wartości są unikalne, zestaw HashSet może być najlepszą klasą zestawu do użycia.

JoshL
źródło
-3

To świetne odpowiedzi, które wydają się obejmować podstawy.

Kolejną kwestią, którą przedstawię, jest to, że słowniki (w języku C #) są bardziej złożone z punktu widzenia kodowania. Posiadanie zarówno list, jak i słowników w tej samej bazie kodu utrudnia utrzymanie kodu, ponieważ obie metody mają subtelne różnice w wykonywaniu podstawowych operacji, takich jak wyszukiwanie i zestawianie danych obiektu. Moim zdaniem jest to, że o ile nie potrzebujesz słownika z uzasadnionego powodu, skorzystaj z listy.

StephenR
źródło
8
Nie zgadzam się. Słownik / mapa to podstawowa struktura danych, którą każdy inżynier oprogramowania powinien dokładnie znać. Tak czy inaczej: potrzebowałbyś uzasadnionego powodu do użycia dowolnej struktury danych; w tym lista.
Steven Evers,