Ricerca…


Enumerazione di un dizionario

Puoi enumerare attraverso un dizionario in uno dei 3 modi:

Utilizzo delle coppie KeyValue

Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(KeyValuePair<int, string> kvp in dict) 
{
   Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}

Usando le chiavi

Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(int key in dict.Keys)
{
    Console.WriteLine("Key : " + key.ToString() + ", Value : " + dict[key]);
}

Utilizzo dei valori

Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(string s in dict.Values)
{
    Console.WriteLine("Value : " + s);
}

Inizializzazione di un dizionario con un inizializzatore di raccolta

// Translates to `dict.Add(1, "First")` etc.
var dict = new Dictionary<int, string>()
{
    { 1, "First" },
    { 2, "Second" },
    { 3, "Third" }
};

// Translates to `dict[1] = "First"` etc.
// Works in C# 6.0.
var dict = new Dictionary<int, string>()
{
    [1] = "First",
    [2] = "Second",
    [3] = "Third"
};

Aggiunta a un dizionario

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "First");
dict.Add(2, "Second");

// To safely add items (check to ensure item does not already exist - would throw)
if(!dict.ContainsKey(3))
{
   dict.Add(3, "Third");
}

In alternativa, possono essere aggiunti / impostati tramite l'indicizzatore. (Un indicizzatore internamente ha l'aspetto di una proprietà, con un get e un set, ma accetta un parametro di qualsiasi tipo specificato tra parentesi):

Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "First";
dict[2] = "Second";
dict[3] = "Third";

A differenza del metodo Add che genera un'eccezione, se una chiave è già contenuta nel dizionario, l'indicizzatore sostituisce semplicemente il valore esistente.

Per il dizionario thread-safe utilizzare ConcurrentDictionary<TKey, TValue> :

var dict = new ConcurrentDictionary<int, string>();
dict.AddOrUpdate(1, "First", (oldKey, oldValue) => "First");

Ottenere un valore da un dizionario

Dato questo codice di installazione:

var dict = new Dictionary<int, string>()
{
    { 1, "First" },
    { 2, "Second" },
    { 3, "Third" }
};

Potresti voler leggere il valore per la voce con la chiave 1. Se la chiave non esiste, ottenere un valore genererà KeyNotFoundException , quindi potresti voler prima verificarlo con ContainsKey :

if (dict.ContainsKey(1))
    Console.WriteLine(dict[1]);

Questo ha uno svantaggio: cercherete nel vostro dizionario due volte (una volta per verificare l'esistenza e una per leggere il valore). Per un dizionario di grandi dimensioni questo può influire sulle prestazioni. Fortunatamente entrambe le operazioni possono essere eseguite insieme:

string value;
if (dict.TryGetValue(1, out value))
    Console.WriteLine(value);

Crea un dizionario con chiavi Case-Insensivitve.

var MyDict = new Dictionary<string,T>(StringComparison.InvariantCultureIgnoreCase)

ConcurrentDictionary (da .NET 4.0)

Rappresenta una raccolta thread-safe di coppie chiave / valore a cui è possibile accedere simultaneamente da più thread.

Creare un'istanza

La creazione di un'istanza funziona più o meno allo stesso modo con il Dictionary<TKey, TValue> , ad esempio:

var dict = new ConcurrentDictionary<int, string>();

Aggiunta o aggiornamento

Potresti essere sorpreso dal fatto che non esiste un metodo Add , ma invece c'è AddOrUpdate con 2 overload:

(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue) - Aggiunge una coppia chiave / valore se la chiave non esiste già o aggiorna una coppia chiave / valore utilizzando la funzione specificata se la chiave esiste già.

(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory) - Usa le funzioni specificate per aggiungere una coppia chiave / valore al se la chiave non esiste già, o per aggiornare una coppia chiave / valore se la chiave esiste già.

Aggiunta o aggiornamento di un valore, indipendentemente dal valore se era già presente per la chiave data (1):

string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");

Aggiunta o aggiornamento di un valore, ma ora modifica il valore in aggiornamento, in base al valore precedente (1):

string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");

Usando il sovraccarico (2) possiamo anche aggiungere un nuovo valore usando una fabbrica:

string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey, valueOld) => $"{valueOld} Updated");

Ottenere valore

Ottenere un valore è lo stesso del Dictionary<TKey,TValue> :

string value = null;
bool success = dict.TryGetValue(1, out value);

Ottenere o aggiungere un valore

Ci sono due overload di mehod, che otterranno o aggiungeranno un valore in modo thread-safe.

Ottieni valore con la chiave 2 o aggiungi il valore "Second" se la chiave non è presente:

string theValue = dict.GetOrAdd(2, "Second");

Utilizzo di una factory per l'aggiunta di un valore, se il valore non è presente:

string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );

IEnumerable to Dictionary (≥ .NET 3.5)

Crea un dizionario <TKey, TValue> da un oggetto IEnumerable <T> :

using System;
using System.Collections.Generic;
using System.Linq;

public class Fruits
{
    public int Id { get; set; }
    public string Name { get; set; }
}

var fruits = new[]
{ 
    new Fruits { Id = 8 , Name = "Apple" },
    new Fruits { Id = 3 , Name = "Banana" },
    new Fruits { Id = 7 , Name = "Mango" },
};


// Dictionary<int, string>                  key      value
var dictionary = fruits.ToDictionary(x => x.Id, x => x.Name);

Rimozione da un dizionario

Dato questo codice di installazione:

var dict = new Dictionary<int, string>()
{
    { 1, "First" },
    { 2, "Second" },
    { 3, "Third" }
};

Utilizzare il metodo Remove per rimuovere una chiave e il relativo valore associato.

bool wasRemoved = dict.Remove(2);

L'esecuzione di questo codice rimuove la chiave 2 e il suo valore dal dizionario. Remove restituisce un valore booleano che indica se la chiave specificata è stata trovata e rimossa dal dizionario. Se la chiave non esiste nel dizionario, nulla viene rimosso dal dizionario e viene restituito false (non viene generata alcuna eccezione).

Non è corretto provare e rimuovere una chiave impostando il valore per la chiave su null .

dict[2] = null; // WRONG WAY TO REMOVE!

Questo non rimuoverà la chiave. Sostituirà semplicemente il valore precedente con un valore null .

Per rimuovere tutte le chiavi e i valori da un dizionario, utilizzare il metodo Clear .

dict.Clear();

Dopo l'esecuzione di Clear , il Count del dizionario sarà 0, ma la capacità interna rimane invariata.

ContainsKey (TKey)

Per verificare se un Dictionary ha una chiave specifica, puoi chiamare il metodo ContainsKey(TKey) e fornire la chiave del tipo TKey . Il metodo restituisce un valore bool quando la chiave esiste sul dizionario. Per esempio:

var dictionary = new Dictionary<string, Customer>()
{
   {"F1", new Customer() { FirstName = "Felipe", ... } },
   {"C2", new Customer() { FirstName = "Carl", ... } },
   {"J7", new Customer() { FirstName = "John", ... } },
   {"M5", new Customer() { FirstName = "Mary", ... } },
};

E controlla se esiste un C2 sul dizionario:

if (dictionary.ContainsKey("C2")) 
{
   // exists
}

Il metodo ContainsKey è disponibile nella versione generica Dictionary<TKey, TValue> .

Dizionario alla lista

Creazione di un elenco di KeyValuePair:

Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.AddRange(dictionary);

Creazione di un elenco di chiavi:

Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Keys);

Creazione di un elenco di valori:

Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Values);

ConcurrentDictionary aumentato con Lazy'1 riduce il calcolo duplicato

Problema

ConcurrentDictionary brilla quando si tratta di restituire istantaneamente chiavi esistenti dalla cache, per lo più senza blocco e contendenti a livello granulare. Ma cosa succede se la creazione dell'oggetto è davvero costosa, superando il costo del cambio di contesto e si verificano alcuni errori di cache?

Se viene richiesta la stessa chiave da più thread, alla fine verrà aggiunto uno degli oggetti risultanti dalle operazioni di collisione e gli altri verranno gettati via, sprecando la risorsa CPU per creare l'oggetto e la risorsa di memoria per archiviare temporaneamente l'oggetto . Altre risorse potrebbero essere sprecate pure. Questo è veramente brutto.

Soluzione

Possiamo combinare ConcurrentDictionary<TKey, TValue> con Lazy<TValue> . L'idea è che il metodo GetOrAdd ConcurrentDictionary può solo restituire il valore che è stato effettivamente aggiunto alla collezione. Anche gli oggetti Lazy che perdono potrebbero essere sprecati in questo caso, ma non è un problema, dato che l'oggetto Lazy è relativamente poco costoso. La proprietà Value del Lazy perdente non viene mai richiesta, perché siamo intelligenti nel richiedere solo la proprietà Value di quella effettivamente aggiunta alla raccolta, quella restituita dal metodo GetOrAdd:

public static class ConcurrentDictionaryExtensions
{
    public static TValue GetOrCreateLazy<TKey, TValue>(
        this ConcurrentDictionary<TKey, Lazy<TValue>> d,
        TKey key,
        Func<TKey, TValue> factory)
    {
        return
            d.GetOrAdd(
                key,
                key1 =>
                    new Lazy<TValue>(() => factory(key1),
                    LazyThreadSafetyMode.ExecutionAndPublication)).Value;
    }
}

La memorizzazione nella cache degli oggetti XmlSerializer può essere particolarmente costosa e anche all'avvio dell'applicazione vi sono molti conflitti. E c'è di più: se si tratta di serializzatori personalizzati, ci sarà una perdita di memoria anche per il resto del ciclo di vita del processo. L'unico vantaggio di ConcurrentDictionary in questo caso è che per il resto del ciclo di vita del processo non ci saranno blocchi, ma l'avvio dell'applicazione e l'utilizzo della memoria sarebbero inaccettabili. Questo è un lavoro per il nostro ConcurrentDictionary, aumentato con Lazy:

private ConcurrentDictionary<Type, Lazy<XmlSerializer>> _serializers =
    new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();

public XmlSerializer GetSerialier(Type t)
{
    return _serializers.GetOrCreateLazy(t, BuildSerializer);
}

private XmlSerializer BuildSerializer(Type t)
{
    throw new NotImplementedException("and this is a homework");
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow