Chiavi duplicate nei dizionari .NET?

Esistono classi di dizionario nella libreria di classi di base .NET che consentono l’uso di chiavi duplicate? L’unica soluzione che ho trovato è quella di creare, ad esempio, una class come:

Dictionary<string, List> 

Ma questo è abbastanza irritante da usare davvero. In Java, credo che un MultiMap riesca a raggiungere questo objective, ma non riesco a trovare un analogo in .NET.

    Se stai usando .NET 3.5, usa la class Lookup .

    MODIFICA: generalmente si crea una ricerca utilizzando Enumerable.ToLookup . Ciò presuppone che non è necessario modificarlo in seguito, ma in genere lo trovo abbastanza buono.

    Se ciò non funziona per te, non penso ci sia nulla nel framework che possa aiutarti – e usare il dizionario è buono come lo è 🙁

    La class List funziona piuttosto bene per le raccolte di chiavi / valori contenenti duplicati in cui si desidera eseguire un’iterazione sulla raccolta. Esempio:

     List> list = new List>(); // add some values to the collection here for (int i = 0; i < list.Count; i++) { Print(list[i].Key, list[i].Value); } 

    Ecco un modo per farlo con List >

     public class ListWithDuplicates : List> { public void Add(string key, string value) { var element = new KeyValuePair(key, value); this.Add(element); } } var list = new ListWithDuplicates(); list.Add("k1", "v1"); list.Add("k1", "v2"); list.Add("k1", "v3"); foreach(var item in list) { string x = string.format("{0}={1}, ", item.Key, item.Value); } 

    Uscite k1 = v1, k1 = v2, k1 = v3

    Se si utilizzano stringhe come chiavi e valori, è ansible utilizzare System.Collections.Specialized.NameValueCollection , che restituirà un array di valori stringa tramite il metodo GetValues ​​(chiave stringa).

    Ho appena trovato la libreria di PowerCollections che include, tra le altre cose, una class chiamata MultiDictionary. Questo avvolge ordinatamente questo tipo di funzionalità.

    Nota molto importante riguardo l’uso della ricerca:

    È ansible creare un’istanza di una ricerca Lookup(TKey, TElement) chiamando ToLookup su un object che implementa IEnumerable(T)

    Non esiste un costruttore pubblico per creare una nuova istanza di una ricerca Lookup(TKey, TElement) . Inoltre, gli oggetti Lookup(TKey, TElement) sono immutabili, ovvero non è ansible aggiungere o rimuovere elementi o chiavi da un object Lookup(TKey, TElement) dopo che è stato creato.

    (da MSDN)

    Penserei che questo sarebbe uno stopper per la maggior parte degli usi.

    Penso che qualcosa come List> possa fare il lavoro.

    Se si utilizza> = .NET 4, è ansible utilizzare la class Tuple :

     // declaration var list = new List>>(); // to add an item to the list var item = Tuple>("key", new List); list.Add(item); // to iterate foreach(var i in list) { Console.WriteLine(i.Item1.ToString()); } 

    Dai un’occhiata alla class HashBag di C5 .

    In risposta alla domanda originale. Qualcosa come Dictionary> è implementato in una class chiamata MultiMap in The Code Project .

    Puoi trovare maggiori informazioni al seguente link: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

    È abbastanza facile la versione “roll your own” di un dizionario che consente di inserire voci “duplicate key”. Ecco una semplice implementazione approssimativa. Potresti considerare di aggiungere il supporto per la maggior parte (se non tutti) di IDictionary .

     public class MultiMap { private readonly Dictionary> storage; public MultiMap() { storage = new Dictionary>(); } public void Add(TKey key, TValue value) { if (!storage.ContainsKey(key)) storage.Add(key, new List()); storage[key].Add(value); } public IEnumerable Keys { get { return storage.Keys; } } public bool ContainsKey(TKey key) { return storage.ContainsKey(key); } public IList this[TKey key] { get { if (!storage.ContainsKey(key)) throw new KeyNotFoundException( string.Format( "The given key {0} was not found in the collection.", key)); return storage[key]; } } } 

    Un rapido esempio su come usarlo:

     const string key = "supported_encodings"; var map = new MultiMap(); map.Add(key, Encoding.ASCII); map.Add(key, Encoding.UTF8); map.Add(key, Encoding.Unicode); foreach (var existingKey in map.Keys) { var values = map[existingKey]; Console.WriteLine(string.Join(",", values)); } 

    NameValueCollection supporta più valori stringa sotto una chiave (che è anche una stringa), ma è l’unico esempio di cui sono a conoscenza.

    Tendo a creare costrutti simili a quelli del tuo esempio quando mi imbatto in situazioni in cui ho bisogno di quel tipo di funzionalità.

    Quando si utilizza l’ List> , è ansible utilizzare LINQ per eseguire la ricerca:

     List> myList = new List>(); //fill it here var q = from a in myList Where a.Key.Equals("somevalue") Select a.Value if(q.Count() > 0){ //you've got your value } 

    Intendi congruente e non un vero duplicato? Altrimenti una tabella hash non sarebbe in grado di funzionare.

    Congruent significa che due chiavi separate possono hash al valore equivalente, ma le chiavi non sono uguali.

    Ad esempio: diciamo che la funzione hash della tua hashtable era solo hashval = key mod 3. Sia 1 che 4 map su 1, ma sono valori diversi. È qui che entra in gioco la tua idea di lista.

    Quando è necessario cercare 1, il valore è hash su 1, l’elenco viene spostato finché non viene trovata la chiave = 1.

    Se si consente l’inserimento di chiavi duplicate, non si è in grado di differenziare le chiavi che corrispondono a quali valori.

    Mi sono imbattuto in questo post alla ricerca della stessa risposta e non ne ho trovato, quindi ho installato una soluzione di esempio semplice con un elenco di dizionari, sovrascrivendo l’operatore [] per aggiungere un nuovo dizionario all’elenco quando tutti gli altri hanno un dato chiave (set), e restituisce un elenco di valori (get).
    È brutto e inefficiente, SOLO ottiene / imposta per chiave e restituisce sempre una lista, ma funziona:

      class DKD { List> dictionaries; public DKD(){ dictionaries = new List>();} public object this[string key]{ get{ string temp; List valueList = new List(); for (int i = 0; i < dictionaries.Count; i++){ dictionaries[i].TryGetValue(key, out temp); if (temp == key){ valueList.Add(temp);}} return valueList;} set{ for (int i = 0; i < dictionaries.Count; i++){ if (dictionaries[i].ContainsKey(key)){ continue;} else{ dictionaries[i].Add(key,(string) value); return;}} dictionaries.Add(new Dictionary()); dictionaries.Last()[key] =(string)value; } } } 

    Il modo in cui utilizzo è solo un

    Dictionary>

    In questo modo hai una sola chiave contenente un elenco di stringhe.

    Esempio:

     List value = new List(); if (dictionary.Contains(key)) { value = dictionary[key]; } value.Add(newValue); 

    Ho modificato la risposta di @Hector Correa in un’estensione con tipi generici e ho aggiunto anche un TryGetValue personalizzato.

      public static class ListWithDuplicateExtensions { public static void Add(this List> collection, TKey key, TValue value) { var element = new KeyValuePair(key, value); collection.Add(element); } public static int TryGetValue(this List> collection, TKey key, out IEnumerable values) { values = collection.Where(pair => pair.Key.Equals(key)).Select(pair => pair.Value); return values.Count(); } } 

    Questo è un rimorchio dizionario Concurrent Penso che questo ti aiuterà:

     public class HashMapDictionary : System.Collections.IEnumerable { private System.Collections.Concurrent.ConcurrentDictionary> _keyValue = new System.Collections.Concurrent.ConcurrentDictionary>(); private System.Collections.Concurrent.ConcurrentDictionary> _valueKey = new System.Collections.Concurrent.ConcurrentDictionary>(); public ICollection Keys { get { return _keyValue.Keys; } } public ICollection Values { get { return _valueKey.Keys; } } public int Count { get { return _keyValue.Count; } } public bool IsReadOnly { get { return false; } } public List this[T1 index] { get { return _keyValue[index]; } set { _keyValue[index] = value; } } public List this[T2 index] { get { return _valueKey[index]; } set { _valueKey[index] = value; } } public void Add(T1 key, T2 value) { lock (this) { if (!_keyValue.TryGetValue(key, out List result)) _keyValue.TryAdd(key, new List() { value }); else if (!result.Contains(value)) result.Add(value); if (!_valueKey.TryGetValue(value, out List result2)) _valueKey.TryAdd(value, new List() { key }); else if (!result2.Contains(key)) result2.Add(key); } } public bool TryGetValues(T1 key, out List value) { return _keyValue.TryGetValue(key, out value); } public bool TryGetKeys(T2 value, out List key) { return _valueKey.TryGetValue(value, out key); } public bool ContainsKey(T1 key) { return _keyValue.ContainsKey(key); } public bool ContainsValue(T2 value) { return _valueKey.ContainsKey(value); } public void Remove(T1 key) { lock (this) { if (_keyValue.TryRemove(key, out List values)) { foreach (var item in values) { var remove2 = _valueKey.TryRemove(item, out List keys); } } } } public void Remove(T2 value) { lock (this) { if (_valueKey.TryRemove(value, out List keys)) { foreach (var item in keys) { var remove2 = _keyValue.TryRemove(item, out List values); } } } } public void Clear() { _keyValue.Clear(); _valueKey.Clear(); } IEnumerator IEnumerable.GetEnumerator() { return _keyValue.GetEnumerator(); } } 

    esempi:

     public class TestA { public int MyProperty { get; set; } } public class TestB { public int MyProperty { get; set; } } HashMapDictionary hashMapDictionary = new HashMapDictionary(); var a = new TestA() { MyProperty = 9999 }; var b = new TestB() { MyProperty = 60 }; var b2 = new TestB() { MyProperty = 5 }; hashMapDictionary.Add(a, b); hashMapDictionary.Add(a, b2); hashMapDictionary.TryGetValues(a, out List result); foreach (var item in result) { //do something } 

    U può definire un metodo per build una chiave di stringa Compound in ogni dove tu vuoi usare il dizionario u devi usare questo metodo per build la tua chiave per esempio:

     private string keyBuilder(int key1, int key2) { return string.Format("{0}/{1}", key1, key2); } 

    per usare:

     myDict.ContainsKey(keyBuilder(key1, key2)) 

    Le chiavi duplicate interrompono l’intero contratto del Dizionario. In un dizionario ogni chiave è unica e mappata su un singolo valore. Se si desidera colbind un object a un numero arbitrario di oggetti aggiuntivi, la soluzione migliore potrebbe essere qualcosa di simile a un DataSet (nel linguaggio comune una tabella). Metti le tue chiavi in ​​una colonna e i tuoi valori nell’altra. Questo è molto più lento di un dizionario, ma questo è il tuo compromesso per perdere la capacità di cancellare gli oggetti chiave.

    Anche questo è ansible:

     Dictionary previousAnswers = null; 

    In questo modo, possiamo avere chiavi uniche. Spero che questo funzioni per te.

    Puoi aggiungere le stesse chiavi con casi diversi come:

    key1
    key1
    KEY1
    key1
    key1
    key1

    So che è una risposta fittizia, ma ha funzionato per me.