No parece haber una implementación genérica de OrderedDictionary(que está en el System.Collections.Specializedespacio 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
OrderedDictionaryen 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
OrderedDictionaryno 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 aKeyedCollectionpara 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
TKeyes 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.Collectionstambié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 implementaIListperoICollection<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.OrderedDictionaryno 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
Addmétodos.pairList.Add(new KeyValuePair<K,V>())(es decir, el método en laListclase). En ese caso, elitsIndexdiccionario no se actualiza y ahora la lista y el diccionario no están sincronizados. Podría ocultar elList.Addmétodo creando unnewPairList.Add(KeyValuePair<K,V>)método, o podría usar la composición en lugar de la herencia y simplemente implementar todos losListmétodos nuevamente ... mucho más código ...if (itsindex.Contains(key)) return;al comienzo deAddpara evitar duplicadosUn problema conceptual importante con una versión genérica de
OrderedDictionaryes que los usuarios de aOrderedDictionary<TKey,TValue>esperarían poder indexarlo numéricamente usando unainto 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.Pointhabí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 unaByIndexpropiedad que devolvió unaIndexerestructura que tenía una referencia a el diccionario, y tenía una propiedad indexada cuyo getter y setter llamaríanGetByIndexySetByIndexsobre é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
ByIndexpropiedad 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.ByIndexque sea un miembroMyDictque 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
intlas 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 queOrderedDictionarysimplemente 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 puntoOrderedDictionaryy las implementaciones proporcionadas en las respuestas) ySortedDictionaryes 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 elOutOfMemoryExceptionslanzamiento 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
SortedDictionaryen el orden en que se agregaron ? Eso es lo queOrderedDictionarypermite. 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éricoOrderedDictionarytiene una sobrecarga de 59 bytes.Si paso
sorted=trueal 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).