2015-04-21 12 views
5

Domandaelemento Dizionario accesso

Must loop attraverso tutti gli elementi C# Dictionary essere fatto solo attraverso foreach, e se sì perché?

Oppure, potrei chiedere la mia domanda come: possono Dictionary elementi sono accessibili dalla posizione all'interno dell'oggetto Dictionary (vale a dire, prima elemento, ultima elemento, terzo rispetto allo scorso elemento, ecc)?

Sfondo a questa domanda

Sto cercando di imparare di più su come Dictionary oggetti di lavoro, in modo da apprezzerei aiuto avvolgere la mia mente intorno a questo. Sto imparando su questo, quindi ho diversi pensieri che sono tutti legati a questa domanda. Proverò a presentare in modo appropriato per il formato SO.

Research

In C# array, elementi fa riferimento posizione. In un Dictionary, i valori sono referenziati da chiavi.

Guardando attraverso il documentation on MSDN, vi sono le dichiarazioni

"Ai fini del conteggio, ciascun elemento nel dizionario è trattata come una struttura KeyValuePair che rappresenta un valore e la relativa chiave. L'ordine in cui la gli articoli restituiti non sono definiti. "

Quindi, sembrerebbe che dal momento che gli articoli dell'ordine siano restituiti non è definito, non c'è modo di accedere agli elementi per posizione. Ho letto anche:

"Recupero di un valore utilizzando la sua chiave è molto veloce, vicino a O (1), perché la classe Dictionary è implementato come una tabella hash"

Guardando la documentazione per la HashTable .NET 4.5 class, si fa riferimento all'utilizzo di una dichiarazione foreach per scorrere e gli elementi di ritorno. Ma non vi è alcun riferimento all'utilizzo di una dichiarazione for o, per quella materia, while o qualsiasi altra istruzione di looping.

Inoltre, ho notato che gli elementi Dictionary utilizzano l'interfaccia IEnumerable, che sembra utilizzare l'foreach come l'unico tipo di istruzione per le funzioni di loop.

Pensieri

Quindi, questo significa che Dictionary elementi non possono essere accessibili da "posizione", come array o liste possono?

Se è così, perché esiste una proprietà .Count che restituisce il numero di coppie chiave/valore, eppure nulla che mi consenta di fare riferimento a tali valori per vicinanza al totale? Ad esempio, .Count è 5, perché non è possibile richiedere coppia chiave/valore .Count meno 1?

In che modo foreach è in grado di eseguire il loop su ciascun elemento, ma non ho accesso ai singoli elementi allo stesso modo?

Non esiste un modo per determinare la posizione di un elemento (chiave o valore) in un oggetto , senza utilizzare foreach? Non posso dire, senza mappare gli elementi in una raccolta, se una chiave è la prima chiave in un Dictionary o l'ultima chiave?

This SO question e le eccellenti risposte toccano su questo, ma sto specificamente alla ricerca per vedere se ho must elementi di copia a una matrice o un altro tipo enumerabile, per accedere a elementi specifici in base alla posizione.

Ecco un esempio. Si prega di notare che sono non alla ricerca di un modo per risolvere specificamente questo esempio - è solo a scopo illustrativo delle mie domande. Supponiamo di voler aggiungere tutte le chiavi in ​​un oggetto Dictionary<string, string> a un elenco separato da virgole, senza virgole alla fine. Con una serie che potevo fare:

string[] arrayStr = new string[2] { "Test1", "Test2" }; 
string outStr = ""; 
for (int i = 0; i < arrayStr.Length; i++) 
{ 
    outStr += arrayStr[i]; 
    if (i < arrayStr.Length - 1) 
    { 
     outStr += ", "; 
    } 
} 

con Dictionary<string, string>, come potrei copiare ogni chiave per outStr utilizzando il metodo di cui sopra? Sembra che dovrei usare foreach. Ma quali sono i metodi o le proprietà Dictionary che mi consentono di identificare dove si trova un elemento all'interno di un dizionario?

Se stai ancora leggendo questo, voglio anche sottolineare che non sto cercando di dire che c'è qualcosa di sbagliato con Dictionary ... Sto semplicemente cercando di capire come questo strumento nel quadro .NET funziona e come usarlo al meglio.

+0

Se l'ordine è importante, utilizzare un altro tipo di raccolta diverso da un dizionario come elenco, pila, coda, matrice o [whathaveyou] (https://msdn.microsoft.com/en-us/library/System.Collections.Generic (v = vs.110) .aspx). Utilizzare un dizionario quando è richiesta una chiave di ricerca O (1) => valore. Non vedo perché ti interesserebbe l'elemento '" Count-1 "' 'th ad esempio; se sei un dizionario è * non * il tipo di raccolta che desideri utilizzare. – RobIII

+0

L'ordine non è definito, quindi anche il valore di un indice specifico non è definito. L'unica ragione per cui ho potuto vedere volendo ottenere un indice specifico è l'accesso casuale agli elementi, e probabilmente c'è un modo migliore per farlo. – clcto

+0

'Con il dizionario , come dovrei copiare ogni chiave in outStr usando il metodo sopra?' Presonalmente non userei quel metodo. 'var list = dictionary.Select (k => k.Key) .ToList();' è più semplice. – Ulric

risposta

3

Diverse risposte corrette qui, ma ho pensato che ti avrebbe fatto piacere una versione breve :)

Sotto il cofano, la classe Dictionary ha un campo privato chiamato buckets. È solo un ordinario array che mappa le posizioni dei numeri interi agli oggetti che hai aggiunto allo Dictionary.

Quando si aggiunge una coppia chiave/valore allo Dictionary, viene calcolato un valore di hash per la chiave. Il valore hash viene utilizzato come indice nell'array buckets.Lo Dictionary utilizza tanti bit dell'hash di cui ha bisogno per garantire che l'indice nell'array buckets non entri in collisione con una voce esistente. L'array buckets verrà espanso secondo necessità a causa di collisioni.

Sì, è possibile tramite riflessione (che consente di estrarre campi dati privati) per ottenere il 3o, o il 4o o il nono membro dell'array buckets. Ma l'array potrebbe essere ridimensionato in qualsiasi momento e non è nemmeno garantito che i dettagli di implementazione di Dictionary non cambino.

+0

Eccellente, grazie! –

8

Supponiamo di avere quattro auto di colori diversi. E vuoi essere in grado di trovare rapidamente la chiave di un'auto per il suo colore. Quindi fai 4 buste etichettate "rosso", "blu", "nero" e "bianco" e posiziona la chiave su ciascuna auto nella busta giusta. Qual è la "prima" macchina? Qual è il "terzo"? Non sei preoccupato per l'ordine delle buste; sei preoccupato di riuscire a ottenere rapidamente la chiave dal colore.

Quindi, questo significa che gli elementi del dizionario non sono accessibili da "posizione", come possono essere gli array o le liste?

Non direttamente, n. È possibile utilizzare Skip e Take ma tutto ciò che faranno è iterare fino ad arrivare all'elemento "n".

Se è così, perché c'è una proprietà .Count che restituisce il numero di coppie chiave/valore, eppure nulla che mi consenta di fare riferimento a queste per vicinanza al totale? Ad esempio, .Count è 5, perché non posso richiedere una coppia chiave/valore .Count meno 1?

È ancora possibile misurare il numero di articoli anche se non ci sono ordini. Nel mio esempio sai che ci sono 4 buste, ma non c'è il concetto della "terza" busta.

In che modo è possibile eseguire il ciclo su ciascun elemento, ma non ho accesso ai singoli elementi allo stesso modo?

Poiché foreach uso IEnumerable, che chiede solo per l'elemento "next" ogni volta -. Collezione sottostante determina quale ordine gli elementi vengono restituiti nel È possibile ritirare le buste una ad una, ma l'ordine è irrilevante .

Non esiste un modo per determinare la posizione di un elemento (chiave o valore) in un oggetto dizionario, senza utilizzare foreach?

È possibile dedurre esso utilizzando foreach e contando quanti elementi si ha prima di raggiungere quello che si desidera, ma non appena si aggiungono o rimuovono un elemento, questa posizione potrebbe cambiare. Se compro una macchina verde e aggiungo la busta, dove nell '"ordine" andrebbe?

Sto specificatamente cercando di vedere se devo copiare elementi in un array o altro tipo enumerabile, per accedere a elementi specifici per posizione.

Beh, no, è possibile utilizzare Skip e Take, ma non c'è modo di prevedere ciò che reca in quella posizione. Puoi prendere due buste, ignorarle, prenderne un'altra e chiamarla la "terza" busta, ma allora?

1

C'è un gran numero di collezioni disponibili nel .Net framework. Devi analizzare i tuoi requisiti e decidere quale collezione utilizzare:

  • Avete bisogno di coppie chiave/valore o solo di articoli?
  • È importante che gli elementi siano ordinati?
  • Avete bisogno di un inserimento veloce o aggiungete semplicemente all'inizio/fine della raccolta?
  • Avete bisogno di un recupero veloce: O (1) o O (log n)?
  • Hai bisogno di un indice, ad esempio di accedere agli elementi in base alla posizione intera?

Per la maggior parte delle combinazioni di questi requisiti esiste una raccolta specializzata.

Nel tuo caso: coppie chiave/valore e accesso attraverso un indice: SortedList

2

Oltre alla risposta di D Stanley, vorrei aggiungere che si dovrebbe verificare SortedDictionary<TKey, TValue>. Memorizza le coppie chiave/valore in una struttura dati che fa mantiene le chiavi ordinate.

var d = new SortedDictionary<int, string>(); 
d.Add(4, "banana"); 
d.Add(2, "apple"); 
d.Add(7, "pineapple"); 
Console.WriteLine(d.ElementAt(1).Value); // banana 
+0

Questo dovrebbe essere un commento sulla risposta di D Stanley; non risponde alla domanda. –

2

Non è necessario eseguire il looping di un dizionario utilizzando foreach, ma i termini 'first,' e 'last' non hanno significato in termini di dizionario, perché l'ordine non è garantito e non è in alcun modo correlato all'ordine gli oggetti sono aggiunti al tuo dizionario.

Pensaci in questo modo. Hai una borsa che stai usando per memorizzare i blocchi, e su ogni blocco c'è un'etichetta unica. Getta in un blocco con le etichette "Foo", "Bar" e "Baz". Ora, se mi chiedi quale sia il conto della mia borsa, posso dire che ho 3 blocchi e se mi chiedi il blocco etichettato "Bar", posso prenderlo per te. Tuttavia, se mi chiedi il primo blocco, non so cosa intendi. I blocchi sono solo un guazzabuglio dentro la mia borsa. Se, invece, dici blocco "Foreach", mi piacerebbe scattare una foto, ti consegnerò ogni blocco, 1 per 1. Di nuovo, l'ordine non è garantito. Sto solo allungando la mano nella mia borsa e tirando fuori ogni blocco finché non ho ottenuto ognuno di loro.

È inoltre possibile chiedere una raccolta di tutte le chiavi in ​​un dizionario, quindi utilizzare ciascun tasto per accedere agli elementi in un dizionario. Tuttavia, ancora una volta, l'ordine delle chiavi non è garantito e, in teoria, potrebbe cambiare ogni volta che si accede ad esso (in pratica, l'ordine delle chiavi .NET è normalmente piuttosto stabile).

Ci sono molti motivi per cui un dizionario è memorizzato in questo modo, ma la cosa fondamentale è che i dizionari devono avere un ordine non specificato per avere sia l'accesso O (1) che l'accesso O (1). Un array, che ha un ordine specificato, ha l'accesso O (1) (è possibile ottenere l'elemento n in un passo), ma gli inserimenti sono O (n).

Problemi correlati