¿Duplicar claves en diccionarios .NET?

¿Hay alguna clase de diccionario en la biblioteca de clases base .NET que permita el uso de claves duplicadas? La única solución que he encontrado es crear, por ejemplo, una clase como:

Dictionary<string, List> 

Pero esto es bastante irritante de usar realmente. En Java, creo que un MultiMap logra esto, pero no puede encontrar un análogo en .NET.

    Si usa .NET 3.5, use la clase de Lookup .

    EDITAR: generalmente creas una Lookup usando Enumerable.ToLookup . Esto supone que no es necesario cambiarlo después, pero normalmente creo que es lo suficientemente bueno.

    Si eso no funciona para usted, no creo que haya nada en el marco que pueda ayudar, y usar el diccionario es lo mejor posible 🙁

    La clase List en realidad funciona bastante bien para colecciones de clave / valor que contienen duplicados donde desea iterar sobre la colección. Ejemplo:

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

    Aquí hay una forma de hacerlo 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); } 

    Salidas k1 = v1, k1 = v2, k1 = v3

    Si usa cadenas como las claves y los valores, puede usar System.Collections.Specialized.NameValueCollection , que devolverá una matriz de valores de cadena mediante el método GetValues ​​(clave de cadena).

    Acabo de encontrar la biblioteca PowerCollections que incluye, entre otras cosas, una clase llamada MultiDictionary. Esto envuelve cuidadosamente este tipo de funcionalidad.

    Nota muy importante con respecto al uso de la búsqueda:

    Puede crear una instancia de una Lookup(TKey, TElement) llamando a ToLookup en un objeto que implementa IEnumerable(T)

    No hay un constructor público para crear una nueva instancia de una Lookup(TKey, TElement) . Además, los objetos Lookup(TKey, TElement) son inmutables, es decir, no se pueden agregar o eliminar elementos o claves de un objeto Lookup(TKey, TElement) después de que se haya creado.

    (desde MSDN)

    Creo que esto sería un stopper para la mayoría de los usos.

    Creo que algo como List> haría el trabajo.

    Si está utilizando> = .NET 4, puede usar Tuple Class:

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

    Eche un vistazo a la clase HashBag de C5 .

    En respuesta a la pregunta original. Algo como Dictionary> se implementa en una clase llamada MultiMap en The Code Project .

    Puede encontrar más información en el siguiente enlace: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

    Es bastante fácil “lanzar su propia versión” de un diccionario que permite entradas de “clave duplicada”. Aquí hay una implementación simple y aproximada. Es posible que desee considerar la posibilidad de agregar soporte básicamente para la mayoría (si no para todos) en 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 ejemplo rápido sobre cómo 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 admite múltiples valores de cadena bajo una clave (que también es una cadena), pero es el único ejemplo que conozco.

    Tiendo a crear construcciones similares a la de su ejemplo cuando me encuentro con situaciones en las que necesito ese tipo de funcionalidad.

    Al usar la List> , puede usar LINQ para hacer la búsqueda:

     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 } 

    ¿Te refieres a un duplicado congruente y no real? De lo contrario, una tabla hash no podría funcionar.

    Congruente significa que dos claves separadas pueden hacer hash al valor equivalente, pero las claves no son iguales.

    Por ejemplo: supongamos que la función hash de su hashtable es simplemente hashval = key mod 3. Tanto 1 como 4 se asignan a 1, pero son valores diferentes. Aquí es donde entra en juego tu idea de una lista.

    Cuando necesite buscar 1, ese valor se transfiere a 1, la lista se atraviesa hasta que se encuentre la clave = 1.

    Si permitió la inserción de claves duplicadas, no podrá diferenciar qué teclas se asignan a qué valores.

    Me tropecé con esta publicación en busca de la misma respuesta, y no encontré ninguna, así que preparé una solución de ejemplo simple usando una lista de diccionarios, anulando el operador [] para agregar un nuevo diccionario a la lista cuando todos los demás tienen un clave dada (establecer), y devolver una lista de valores (obtener).
    Es feo e ineficiente, SOLO obtiene / establece por clave, y siempre devuelve una lista, pero funciona:

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

    La forma en que uso es solo una

    Dictionary>

    De esta forma, tiene una sola clave que contiene una lista de cadenas.

    Ejemplo:

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

    Cambié la respuesta de @Hector Correa en una extensión con tipos generics y también agregué un TryGetValue personalizado.

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

    Esta es una forma de arrastre Diccionario concurrente Creo que esto te ayudará a:

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

    ejemplos:

     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 puede definir un método para construir una clave de cadena compuesta donde quiera usar el diccionario u debe usar este método para construir su clave, por ejemplo:

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

    Para usar:

     myDict.ContainsKey(keyBuilder(key1, key2)) 

    Las llaves duplicadas rompen todo el contrato del Diccionario. En un diccionario, cada clave es única y se asigna a un único valor. Si desea vincular un objeto a una cantidad arbitraria de objetos adicionales, la mejor opción podría ser algo similar a un DataSet (en el lenguaje común una tabla). Coloque sus llaves en una columna y sus valores en la otra. Esto es significativamente más lento que un diccionario, pero esa es su compensación por perder la capacidad de hash de los objetos clave.

    También esto es posible:

     Dictionary previousAnswers = null; 

    De esta manera, podemos tener claves únicas. Espero que esto funcione para usted.

    Puede agregar las mismas teclas con diferentes casos como:

    key1
    Key1
    KEY1
    KeY1
    kEy1
    keY1

    Sé que es una respuesta tonta, pero funcionó para mí.