c# dictionary - Qual è il modo migliore per scorrere su un dizionario?





new all (21)


Dipende se stai cercando le chiavi o i valori ...

Dal Dictionary(TKey, TValue) MSDN Dictionary(TKey, TValue) Descrizione della classe:

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

Ho visto alcuni modi diversi per scorrere su un dizionario in C #. C'è un modo standard?




Il modo standard per scorrere su un dizionario, secondo la documentazione ufficiale su MSDN è:

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



Usando C # 7 , aggiungi questo metodo di estensione a qualsiasi progetto della tua soluzione:

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


E usa questa semplice sintassi

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


O questo, se preferisci

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


Al posto del tradizionale

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

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


Il metodo di estensione trasforma KeyValuePair del tuo IDictionary<TKey, TValue> in una tuple fortemente tipizzata, permettendoti di usare questa nuova sintassi confortevole.

Converte le sole voci del dizionario richieste in tuples , quindi NON converte l'intero dizionario in tuples , quindi non ci sono problemi di prestazioni correlati a questo.

C'è solo un costo minore che chiama il metodo di estensione per creare una tuple in confronto all'uso diretto di KeyValuePair , il che NON dovrebbe essere un problema se si assegnano le proprietà Key e Value KeyValuePair alle nuove variabili di loop.

In pratica, questa nuova sintassi si adatta molto bene alla maggior parte dei casi, ad eccezione degli scenari di prestazioni ultraveloce a basso livello, in cui è ancora possibile scegliere di non utilizzarlo in quel determinato punto.

Dai un'occhiata a questo: Blog MSDN - Nuove funzionalità in C # 7




Apprezzo che questa domanda abbia già avuto molte risposte, ma ho voluto lanciare una piccola ricerca.

L'iterazione su un dizionario può essere piuttosto lenta se confrontata con l'iterazione su qualcosa come un array. Nei miei test un'iterazione su un array ha richiesto 0,015003 secondi mentre un'iterazione su un dizionario (con lo stesso numero di elementi) ha impiegato 0,0365073 secondi per 2,4 volte il tempo! Anche se ho visto differenze molto più grandi. Per fare un confronto una lista era da qualche parte nel mezzo a 0.00215043 secondi.

Tuttavia, è come paragonare mele e arance. Il mio punto è che iterare sui dizionari è lento.

I dizionari sono ottimizzati per le ricerche, quindi con questo in mente ho creato due metodi. Uno semplicemente fa un foreach, l'altro itera le chiavi quindi alza lo sguardo.

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

    return "Normal";
}

Questo carica le chiavi e scorre su di esse (ho anche provato a tirare le chiavi in ​​una stringa [] ma la differenza era trascurabile.

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

Con questo esempio, il normale test foreach ha richiesto 0,0310062 e la versione delle chiavi ha richiesto 0,2205441. Il caricamento di tutte le chiavi e l'iterazione su tutte le ricerche è chiaramente molto più lento!

Per un test finale ho eseguito la mia iterazione dieci volte per vedere se ci sono dei benefici nell'usare le chiavi qui (a questo punto ero solo curioso):

Ecco il metodo RunTest se questo ti aiuta a visualizzare cosa sta succedendo.

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

Qui la normale run foreach ha richiesto 0,2820564 secondi (circa dieci volte di più rispetto a una singola iterazione, come previsto). L'iterazione sui tasti ha richiesto 2.224.9449 secondi.

Modificato da aggiungere: leggere alcune delle altre risposte mi ha fatto dubitare di cosa sarebbe successo se avessi usato il dizionario anziché il dizionario. In questo esempio la matrice ha impiegato 0,0120024 secondi, l'elenco 0,0185037 secondi e il dizionario 0,0465093 secondi. È ragionevole aspettarsi che il tipo di dati faccia la differenza su quanto più lento è il dizionario.

Quali sono le mie conclusioni ?

  • Evitare di scorrere su un dizionario, se possibile, sono sostanzialmente più lento di iterare su un array con gli stessi dati in esso contenuti.
  • Se si sceglie di eseguire iterazioni su un dizionario, non cercare di essere troppo intelligente, anche se è più lento fare molto peggio dell'utilizzo del metodo standard foreach.



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



Forma più semplice per iterare un dizionario:

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



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

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



Hai suggerito di seguito per iterare

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

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

Cordiali saluti, foreach non funziona se il valore è di tipo oggetto.




I dizionari sono liste speciali, mentre ogni valore nella lista ha una chiave che è anche una variabile. Un buon esempio di un dizionario è una rubrica.

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

Si noti che quando si definisce un dizionario, è necessario fornire una definizione generica con due tipi: il tipo di chiave e il tipo del valore. In questo caso, la chiave è una stringa mentre il valore è un numero intero.

Ci sono anche due modi per aggiungere un singolo valore al dizionario, usando l'operatore delle parentesi o usando il metodo Aggiungi.

Per verificare se un dizionario ha una certa chiave in esso, possiamo usare il metodo 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"]);
}

Per rimuovere un elemento da un dizionario, possiamo usare il metodo Rimuovi. Rimozione di un elemento da un dizionario tramite la sua chiave è veloce e molto efficiente. Quando si rimuove un elemento da un elenco utilizzando il suo valore, il processo è lento e inefficiente, a differenza della funzione Rimuovi dizionario.

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

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



Ci sono molte opzioni. Il mio preferito è KeyValuePair

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

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

Puoi anche utilizzare le raccolte di chiavi e valori




Dizionario <TKey, TValue> È una classe di raccolta generica in c # e memorizza i dati nel formato del valore della chiave.Il tasto deve essere univoco e non può essere nullo mentre il valore può essere duplicato e nullo.Come ogni voce nel dizionario è trattato come struttura KeyValuePair <TKey, TValue> che rappresenta una chiave e il suo valore. e quindi dovremmo prendere il tipo di elemento KeyValuePair <TKey, TValue> durante l'iterazione dell'elemento. Di seguito è riportato l'esempio.

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



Con .NET Framework 4.7 si può usare la decomposizione

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

Per fare in modo che questo codice funzioni su versioni C # inferiori, aggiungi il System.ValueTuple NuGet package e scrivi da qualche parte

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



A partire da C # 7, puoi decostruire gli oggetti in variabili. Credo che questo sia il modo migliore per scorrere su un dizionario.

Esempio:

Crea un metodo di estensione su KeyValuePair<TKey, TVal> che lo decostruisce:

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

Dictionary<TKey, TVal> su qualsiasi Dictionary<TKey, TVal> nel modo seguente

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



Puoi anche provare questo su grandi dizionari per l'elaborazione multithread.

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






In alcuni casi potrebbe essere necessario un contatore che può essere fornito dall'implementazione del ciclo. Per questo, LINQ fornisce ElementAt che consente quanto segue:

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



In generale, chiedere "il modo migliore" senza un contesto specifico è come chiedere qual è il colore migliore.

Da un lato, ci sono molti colori e non c'è il miglior colore. Dipende dal bisogno e spesso anche dal gusto.

D'altra parte, ci sono molti modi per scorrere su un dizionario in C # e non c'è il modo migliore. Dipende dal bisogno e spesso anche dal gusto.

Il modo più diretto

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

Se è necessario solo il valore (consente di chiamarlo item , più leggibile di kvp.Value ).

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

Se hai bisogno di un ordine specifico

Generalmente, i principianti sono sorpresi sull'ordine di enumerazione di un dizionario.

LINQ fornisce una sintassi concisa che consente di specificare l'ordine (e molte altre cose), ad esempio:

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

Anche in questo caso potresti aver bisogno solo del valore. LINQ fornisce anche una soluzione concisa per:

  • iterare direttamente sul valore (consente di chiamarlo item , più leggibile di kvp.Value )
  • ma ordinati per le chiavi

Ecco qui:

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

Ci sono molti altri casi d'uso del mondo reale che puoi fare da questi esempi. Se non hai bisogno di un ordine specifico, attenersi semplicemente al "modo più diretto" (vedi sopra)!




Direi che foreach è il modo standard, anche se ovviamente dipende da cosa stai cercando

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

E 'quello che stai cercando?




oltre ai post più alti in cui c'è una discussione tra l'utilizzo

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

o

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

il più completo è il seguente perché è possibile visualizzare il tipo di dizionario dall'inizializzazione, 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
}



Se, per esempio, si desidera eseguire un'iterazione sulla raccolta di valori per impostazione predefinita, credo che sia possibile implementare IEnumerable <>, dove T è il tipo di valori oggetto nel dizionario e "this" è un dizionario.

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



Preferisco i tipi .NET maiuscolo (piuttosto che gli alias) per motivi di formattazione. I tipi .NET sono colorati come gli altri tipi di oggetto (i tipi di valore sono oggetti appropriati, dopo tutto).

Le parole chiave condizionali e di controllo (come if , switch e return ) sono in minuscolo e di colore blu scuro (impostazione predefinita). E preferirei non avere il disaccordo in uso e formato.

Tenere conto:

String someString; 
string anotherString; 






c# dictionary loops