No parece haber una implementación genérica de OrderedDictionary
(que está en el System.Collections.Specialized
espacio de nombres) en .NET 3.5. ¿Hay alguno que me estoy perdiendo?
He encontrado implementaciones para proporcionar la funcionalidad, pero me preguntaba si / por qué no hay una implementación genérica lista para usar y si alguien sabe si es algo en .NET 4.0.
OrderedDictionary<T>
: codeproject.com/Articles/18615/…Systems.Collections.Generic
. SolicitemosOrderedDictionary<TKey,TValue>
.NET 5. Como otros han señalado, el caso de que la clave sea int es degenerada y necesitará cuidados especiales.Respuestas:
Tienes razón. No hay un equivalente genérico
OrderedDictionary
en el marco en sí.(Ese es el caso de .NET 4 también, por lo que yo sé).
¡Pero puedes votarlo en UserVoice de Visual Studio (04/10/2016)!
fuente
Implementar un genérico
OrderedDictionary
no es terriblemente difícil, pero lleva mucho tiempo innecesariamente y, francamente, esta clase es un gran descuido por parte de Microsoft. Hay varias formas de implementar esto, pero elegí usar aKeyedCollection
para mi almacenamiento interno. También elegí implementar varios métodos para ordenar de la manera que loList<T>
hace, ya que esto es esencialmente un IList e IDictionary híbrido. He incluido mi implementación aquí para la posteridad.Aquí está la interfaz. Tenga en cuenta que incluye
System.Collections.Specialized.IOrderedDictionary
, que es la versión no genérica de esta interfaz que fue proporcionada por Microsoft.Aquí está la implementación junto con las clases auxiliares:
Y ninguna implementación estaría completa sin algunas pruebas (pero trágicamente, SO no me permitirá publicar tanto código en una publicación), por lo que tendré que dejarlo para que escriba sus pruebas. Pero, dejé algunos de ellos para que puedas tener una idea de cómo funciona:
- ACTUALIZACIÓN -
Fuente para esta y otras bibliotecas de .NET faltantes realmente útiles aquí: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs
fuente
TKey
es asíint
? ¿Cómothis[]
funcionará en tal caso?Para el registro, hay una KeyedCollection genérica que permite que los objetos sean indexados por un int y una clave. La clave debe estar incrustada en el valor.
fuente
Aquí hay un hallazgo extraño: el espacio de nombres System.Web.Util en System.Web.Extensions.dll contiene un OrderedDictionary genérico
No estoy seguro de por qué MS lo colocó allí en lugar del paquete System.Collections.Generic, pero supongo que simplemente puede copiar y pegar el código y usarlo (es interno, por lo que no puede usarlo directamente). Parece que la implementación utiliza un diccionario estándar y listas de clave / valor separadas. Muy claro...
fuente
System.Runtime.Collections
también contiene un internoOrderedDictionary<TKey, TValue>
que simplemente envuelve la versión no genéricaSystem.Web.Util.OrderedDictionary<TKey, TValue>
se implementa internamente alrededor de genéricosDictionary
. Curiosamente no se implementaIList
peroICollection<KeyValuePair<TKey, TValue>>
List<KeyValuePair<TKey,TValue>>
será competitiva debido al patrón de acceso a la memoria, para tamaños más grandes solo use la misma lista +Dictionary<TKey,int>
como búsqueda. AFAIK no existe una estructura de datos que funcione mejor en términos de velocidad / memoria en BigO.System.Collections.Specialized.OrderedDictionary
no es un tipo genérico. Mira ma, no hay corchetes angulares en la página del documento quePor lo que vale, así es como lo resolví:
Se puede inicializar así:
y accedido así:
fuente
Add
métodos.pairList.Add(new KeyValuePair<K,V>())
(es decir, el método en laList
clase). En ese caso, elitsIndex
diccionario no se actualiza y ahora la lista y el diccionario no están sincronizados. Podría ocultar elList.Add
método creando unnew
PairList.Add(KeyValuePair<K,V>)
método, o podría usar la composición en lugar de la herencia y simplemente implementar todos losList
métodos nuevamente ... mucho más código ...if (itsindex.Contains(key)) return;
al comienzo deAdd
para evitar duplicadosUn problema conceptual importante con una versión genérica de
OrderedDictionary
es que los usuarios de aOrderedDictionary<TKey,TValue>
esperarían poder indexarlo numéricamente usando unaint
o mediante una búsqueda usando aTKey
. Cuando el único tipo de clave eraObject
, como era el caso con los no genéricosOrderedDictionary
, el tipo de argumento pasado al indexador sería suficiente para distinguir si qué tipo de operación de indexación debería realizarse. Sin embargo, tal como están las cosas, no está claro cómoOrderedDictionary<int, TValue>
debería comportarse el indexador de un .Si las clases como
Drawing.Point
habían recomendado y seguido una regla de que las estructuras mutables por partes deberían exponer sus elementos mutables como campos en lugar de propiedades, y abstenerse de usar establecedores de propiedades que modifiquenthis
, entoncesOrderedDictionary<TKey,TValue>
podría exponer eficientemente unaByIndex
propiedad que devolvió unaIndexer
estructura que tenía una referencia a el diccionario, y tenía una propiedad indexada cuyo getter y setter llamaríanGetByIndex
ySetByIndex
sobre él. Por lo tanto, se podría decir algo así comoMyDict.ByIndex[5] += 3;
agregar 3 al sexto elemento del diccionario.Desafortunadamente, para que el compilador acepte tal cosa, sería necesario hacer que la
ByIndex
propiedad devuelva una nueva instancia de clase en lugar de una estructura cada vez que se invoca, eliminando las ventajas que se obtendrían al evitar el boxeo.En VB.NET, uno podría solucionar ese problema utilizando una propiedad indexada con nombre (por
MyDict.ByIndex[int]
lo que sería un miembroMyDict
, en lugar de requerirMyDict.ByIndex
que sea un miembroMyDict
que incluya un indexador), pero C # no permite tales cosas.Todavía podría haber valido la pena ofrecer un
OrderedDictionary<TKey,TValue> where TKey:class
, pero en gran parte la razón para proporcionar genéricos en primer lugar fue permitir su uso con tipos de valor.fuente
int
las teclas de tipo presentan un desafío, pero podría evitarse siguiendo el ejemplo delSortedList<TKey, TValue>
tipo relacionado : solo admite teclas con[...]
, y requiere el uso de.Values[...]
para acceso por índice. (SortedDictionary<TKey, TValue>
por el contrario, que no está optimizado para el acceso indexado, requiere el uso de.ElementAt(...).Value
)Para muchos propósitos, he encontrado que uno puede sobrevivir con un
List<KeyValuePair<K, V>>
. (No, si lo necesita para ampliarDictionary
, obviamente, y no si necesita algo mejor que la búsqueda de valor-clave O (n)).fuente
Tuple<T1, T2>
si no tienen una relación clave-valor.Existe
SortedDictionary<TKey, TValue>
. Aunque semánticamente cerca, no estoy afirmando que sea lo mismo queOrderedDictionary
simplemente porque no lo son. Incluso a partir de las características de rendimiento. Sin embargo, la diferencia muy interesante y bastante importante entreDictionary<TKey, TValue>
(y hasta ese puntoOrderedDictionary
y las implementaciones proporcionadas en las respuestas) ySortedDictionary
es que este último está utilizando un árbol binario debajo. Esta es una distinción crítica porque hace que la clase sea inmune a las restricciones de memoria aplicadas a la clase genérica. Vea este hilo sobre elOutOfMemoryExceptions
lanzamiento cuando la clase genérica se utiliza para manejar un gran conjunto de pares clave-valor.¿Cómo calcular el valor máximo para el parámetro de capacidad pasado al constructor del Diccionario para evitar OutOfMemoryException?
fuente
SortedDictionary
en el orden en que se agregaron ? Eso es lo queOrderedDictionary
permite. Concepto diferente al ordenado . (He cometido este error en el pasado; pensé que proporcionar un comparador al constructor OrderedDictionary lo ordenaría, pero todo lo que hace con el comparador es determinar la igualdad de clave; por ejemplo, el comparador insensible a cadenas permite la búsqueda de claves insensibles a cadenas)Correcto, es una omisión desafortunada. Echo de menos el Orden ordenado de Python
Entonces escribí mi propia
OrderedDictionary<K,V>
clase en C #. ¿Como funciona? Mantiene dos colecciones: un diccionario vainilla desordenado y una lista ordenada de claves. Con esta solución, las operaciones de diccionario estándar mantienen su complejidad rápida, y la búsqueda por índice también es rápida.https://gist.github.com/hickford/5137384
Aquí está la interfaz
fuente
Como seguimiento al comentario de @VB, aquí hay una implementación accesible de System.Runtime.Collections.OrderedDictionary <,> . Originalmente iba a acceder a él por reflexión y proporcionarlo a través de una fábrica, pero el dll en el que se encuentra no parece ser muy accesible, así que simplemente saqué la fuente en sí.
Una cosa a tener en cuenta es que el indexador aquí no arrojará
KeyNotFoundException
. Absolutamente odio esa convención y esa fue la primera libertad que tomé en esta implementación. Si eso es importante para usted, simplemente reemplace la línea parareturn default(TValue);
. Utiliza C # 6 ( compatible con Visual Studio 2013 )Solicitudes de extracción / discusión aceptadas en GitHub
fuente
Para aquellos que buscan una opción de paquete "oficial" en NuGet, se ha aceptado una implementación de un OrderedDictionary genérico en .NET CoreFX Lab. Si todo va bien, el tipo eventualmente será aprobado e integrado al repositorio principal de .NET CoreFX.
Existe la posibilidad de que esta implementación sea rechazada.
La implementación comprometida se puede consultar aquí https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/OrderedDictionary.cs
El paquete NuGet que definitivamente tiene este tipo disponible para su uso se puede encontrar aquí https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3
O puede instalar el paquete dentro de Visual Studio. Busque el paquete "Microsoft.Experimental.Collections" y asegúrese de que la casilla de verificación "Incluir versión preliminar" esté seleccionada.
Actualizará esta publicación siempre que el tipo esté oficialmente disponible.
fuente
Implementé un genérico
OrderedDictionary<TKey, TValue>
envolviendoSortedList<TKey, TValue>
y agregando un privadoDictionary<TKey, int>
_order
. Luego creé una implementación interna deComparer<TKey>
, pasando una referencia al diccionario _order. Luego uso este comparador para la SortedList interna. Esta clase mantiene el orden de los elementos pasados al constructor y el orden de las adiciones.Esta implementación tiene casi las mismas características grandes de O que
SortedList<TKey, TValue>
desde que agregar y eliminar a _order es O (1). Cada elemento tomará (de acuerdo con el libro 'C # 4 in a Nutshell', p. 292, tabla 7-1) espacio de memoria adicional de 22 (sobrecarga) + 4 (int orden) + tamaño de la tecla T (supongamos que 8) = 34 Junto conSortedList<TKey, TValue>
la sobrecarga de dos bytes, la sobrecarga total es de 36 bytes, mientras que el mismo libro dice que no genéricoOrderedDictionary
tiene una sobrecarga de 59 bytes.Si paso
sorted=true
al constructor, entonces _order no se usa en absoluto,OrderedDictionary<TKey, TValue>
es exactamenteSortedList<TKey, TValue>
con una sobrecarga menor para el ajuste, si es que tiene sentido.Voy a almacenar no tantos objetos de referencia grandes en el
OrderedDictionary<TKey, TValue>
, así que para mí esto ca. Sobrecarga de 36 bytes es tolerable.El código principal está abajo. El código actualizado completo está en esta esencia .
fuente
OrderedDictionary
, con respecto a las inserciones o eliminaciones: (1) Nunca habrá eliminaciones; (2) habrá eliminaciones, pero lo importante es que los elementos se enumeren en el orden agregado; no hay necesidad de acceso por índice; (3) el índice numérico de un artículo debe (o al menos puede) permanecer constante, y no se agregarán más de 2 mil millones de artículos durante la vida útil de la colección, por lo que si se elimina el artículo # 7, nunca más habrá un artículo 7; (4) el índice de un artículo debe ser su rango con respecto a los sobrevivientes.Dictionary
. Los escenarios # 2 y # 3 podrían manejarse haciendo que cada elemento mantenga un índice que indique cuándo se agregó y los enlaces a los elementos agregados antes o después. El escenario # 4 es el único que parecería que no debería ser capaz de lograr el rendimiento O (1) para operaciones en secuencia arbitraria. Dependiendo de los patrones de uso, # 4 puede ser ayudado mediante el uso de varias estrategias de actualización diferida (mantener los recuentos en un árbol y hacer que los cambios en un nodo invaliden en lugar de actualizar el nodo y sus padres).