Estoy tratando de hacer una tabla de búsqueda de diccionario en C #. Necesito resolver una tupla de valores de 3 en una cadena. Intenté usar matrices como claves, pero eso no funcionó, y no sé qué más hacer. En este punto, estoy considerando hacer un Diccionario de Diccionarios de Diccionarios, pero probablemente no sería muy bonito de ver, aunque así es como lo haría en javascript.
c#
dictionary
hashtable
tuples
AlexH
fuente
fuente
GetHashCode
implementación no es muy buena. Es invariante bajo la permutación de los campos.new object()
igual a otronew object()
? No solo usa una comparación de referencia directa ... intente:bool test = new Tuple<int, string>(1, "foo").Equals(new Tuple<int, string>(1, "Foo".ToLower()));
Entre los enfoques basados en tuplas y diccionarios anidados, casi siempre es mejor optar por tuplas.
Desde el punto de vista de la mantenibilidad ,
es mucho más fácil implementar una funcionalidad que se parece a:
que
desde el lado del destinatario. En el segundo caso, cada adición, búsqueda, eliminación, etc. requiere una acción en más de un diccionario.
Además, si su clave compuesta requiere un campo más (o menos) en el futuro, deberá cambiar mucho el código en el segundo caso (diccionario anidado) ya que debe agregar más diccionarios anidados y verificaciones posteriores.
Desde la perspectiva del desempeño , la mejor conclusión a la que puede llegar es midiéndola usted mismo. Pero hay algunas limitaciones teóricas que puede considerar de antemano:
En el caso del diccionario anidado, tener un diccionario adicional para cada clave (externa e interna) tendrá una sobrecarga de memoria (más de lo que tendría la creación de una tupla).
En el caso del diccionario anidado, todas las acciones básicas, como la adición, actualización, búsqueda, eliminación, etc., deben realizarse en dos diccionarios. Ahora hay un caso en el que el enfoque de diccionario anidado puede ser más rápido, es decir, cuando los datos que se buscan están ausentes, ya que los diccionarios intermedios pueden omitir el cálculo y la comparación del código hash completo, pero de nuevo debe cronometrarse para estar seguro. En presencia de datos, debería ser más lento ya que las búsquedas deberían realizarse dos veces (o tres veces dependiendo del anidamiento).
Con respecto al enfoque de tuplas, las tuplas de .NET no son las más eficaces cuando están destinadas a usarse como claves en conjuntos, ya que su implementación
Equals
yGetHashCode
su implementación provocan un recuadro para los tipos de valor .Yo optaría por un diccionario basado en tuplas, pero si quiero más rendimiento, usaría mi propia tupla con una mejor implementación.
En una nota al margen, pocos cosméticos pueden hacer que el diccionario sea genial:
Las llamadas de estilo indexador pueden ser mucho más limpias e intuitivas. Por ejemplo,
Así que exponga los indexadores necesarios en su clase de diccionario que maneja internamente las inserciones y búsquedas.
Además, implemente una
IEnumerable
interfaz adecuada y proporcione unAdd(TypeA, TypeB, TypeC, string)
método que le brinde la sintaxis del inicializador de la colección, como:fuente
string foo = dict[a][b][c]
:?a
. Podría simplemente iterar cualquier diccionario como cualquier colección normal y verificar la propiedad clave si es asía
. Si siempre desea obtener el elemento en dict por primera propiedad, entonces puede diseñar mejor el diccionario como diccionario de diccionarios como se muestra en mi respuesta y consultar comodict[a]
, lo que le brinda otro diccionario.4
para ambas clavesa
yb
, puede convertirlo en un diccionario estándar y agregar valores comodict[a] = 4
ydict[b] = 4
. Puede que no tenga sentido si lógicamente es tuyoa
yb
debería ser una unidad. En tal caso, puede definir una costumbreIEqualityComparer
que iguale dos instancias clave como iguales si alguna de sus propiedades es igual. Todo esto se puede hacer genéricamente con refelction.Si está en C # 7, debería considerar el uso de tuplas de valor como clave compuesta. Las tuplas de valor suelen ofrecer un mejor rendimiento que las tuplas de referencia tradicionales (
Tuple<T1, …>
) ya que las tuplas de valor son tipos de valor (estructuras), no tipos de referencia, por lo que evitan la asignación de memoria y los costos de recolección de basura. Además, ofrecen una sintaxis más concisa e intuitiva, lo que permite nombrar sus campos si así lo desea. También implementan laIEquatable<T>
interfaz necesaria para el diccionario.fuente
La forma buena, limpia, rápida, fácil y legible es:
agregue algo similar como esto:
Entonces puedes usarlo con el diccionario:
fuente
public TypeA DataA => Item1;
Si por alguna razón realmente desea evitar la creación de su propia clase Tuple o el uso integrado en .NET 4.0, existe otro enfoque posible; puede combinar los tres valores clave en un solo valor.
Por ejemplo, si los tres valores son tipos enteros juntos y no toman más de 64 bits, puede combinarlos en un
ulong
.En el peor de los casos, siempre puede usar una cadena, siempre que se asegure de que los tres componentes que contiene estén delimitados con algún carácter o secuencia que no ocurra dentro de los componentes de la clave, por ejemplo, con tres números que podría intentar:
Obviamente, hay una sobrecarga de composición en este enfoque, pero dependiendo de lo que esté usando para esto, puede ser lo suficientemente trivial como para que no le importe.
fuente
JavaScriptSerializer
para concatenar una matriz de tipos de cadenas y / o enteros por usted. De esta forma, no es necesario que usted mismo cree un carácter delimitador.key1
,key2
,key3
) eran cadenas que contienen la deliminator ("#"
)Anularía su Tuple con un GetHashCode adecuado y lo usaría como clave.
Siempre que sobrecargue los métodos adecuados, debería ver un rendimiento decente.
fuente
Aquí está la tupla .NET como referencia:
fuente
Si su código de consumo puede conformarse con una interfaz IDictionary <>, en lugar de Dictionary, mi instinto habría sido usar un SortedDictionary <> con un comparador de matriz personalizado, es decir:
Y crea así (usando int [] solo por el bien de un ejemplo concreto):
fuente