Não parece haver uma implementação genérica de OrderedDictionary
(que está no System.Collections.Specialized
espaço para nome) no .NET 3.5. Há um que estou perdendo?
Encontrei implementações disponíveis para fornecer a funcionalidade, mas me perguntei se / por que não há uma implementação genérica pronta para uso e se alguém sabe se é algo no .NET 4.0?
OrderedDictionary<T>
: codeproject.com/Articles/18615/…Systems.Collections.Generic
. Vamos solicitar oOrderedDictionary<TKey,TValue>
.NET 5. Como outros já apontaram, o caso da chave é um int degenerado e precisará de cuidados especiais.Respostas:
Você está certo. Não há equivalente genérico de
OrderedDictionary
no próprio framework.(Esse também ainda é o caso do .NET 4, pelo que sei.)
Mas você pode votar no UserVoice do Visual Studio (04-10-2016)!
fonte
A implementação de um genérico
OrderedDictionary
não é terrivelmente difícil, mas consome tempo desnecessariamente e, francamente, essa classe é uma grande supervisão da parte da Microsoft. Existem várias maneiras de implementar isso, mas eu escolhi usar aKeyedCollection
para meu armazenamento interno. Também escolhi implementar vários métodos para classificar da maneira queList<T>
faz, pois esse é essencialmente um IList e um IDictionary híbridos. Eu incluí minha implementação aqui para posteridade.Aqui está a interface. Observe que ele inclui
System.Collections.Specialized.IOrderedDictionary
, que é a versão não genérica dessa interface fornecida pela Microsoft.Aqui está a implementação junto com as classes auxiliares:
E nenhuma implementação seria completa sem alguns testes (mas, tragicamente, o SO não permitirá que eu publique tanto código em um post), então terei que deixar você escrever seus testes. Mas deixei alguns deles para que você pudesse ter uma idéia de como ele funciona:
- ATUALIZAÇÃO -
Fonte para esta e outras bibliotecas principais do .NET ausentes realmente úteis aqui: https://github.com/mattmc3/dotmore/blob/master/dotmore/Collections/Generic/OrderedDictionary.cs
fonte
TKey
forint
? Comothis[]
vai funcionar nesse caso?Para o registro, existe um KeyedCollection genérico que permite que os objetos sejam indexados por um int e uma chave. A chave deve ser incorporada no valor.
fonte
Aqui está uma descoberta bizarra: o espaço para nome System.Web.Util em System.Web.Extensions.dll contém um OrderedDictionary genérico
Não sei por que a MS o colocou lá em vez do pacote System.Collections.Generic, mas presumo que você possa simplesmente copiar e colar o código e usá-lo (é interno, por isso não pode usá-lo diretamente). Parece que a implementação usa um dicionário padrão e listas de chave / valor separadas. Bem direto...
fonte
System.Runtime.Collections
também contém um internoOrderedDictionary<TKey, TValue>
que envolve apenas a versão não genéricaSystem.Web.Util.OrderedDictionary<TKey, TValue>
é implementado internamente em torno de genéricosDictionary
. Estranhamente, ele não implementa,IList
masICollection<KeyValuePair<TKey, TValue>>
List<KeyValuePair<TKey,TValue>>
será competitiva devido ao padrão de acesso à memória, para tamanhos maiores, use a mesma lista +Dictionary<TKey,int>
como uma pesquisa. AFAIK não existe uma estrutura de dados com melhor desempenho em termos de velocidade / memória no BigO.System.Collections.Specialized.OrderedDictionary
não é um tipo genérico. Veja ma, sem colchetes angulares na página do documento que você vinculou: PPelo que vale, aqui está como eu resolvi:
Pode ser inicializado assim:
e acessado assim:
fonte
Add
métodos.pairList.Add(new KeyValuePair<K,V>())
(ou seja, o método naList
classe). Nesse caso, oitsIndex
dicionário não é atualizado e agora a lista e o dicionário estão fora de sincronia. Podia esconder oList.Add
método por criar umnew
PairList.Add(KeyValuePair<K,V>)
método, ou poderia usar composição em vez de herança e apenas implementar todos osList
métodos novamente ... um código muito mais ...if (itsindex.Contains(key)) return;
para o início doAdd
para evitar duplicatasUm grande problema conceitual com uma versão genérica de
OrderedDictionary
é que os usuários de aOrderedDictionary<TKey,TValue>
esperariam poder indexá-lo numericamente usando umint
ou pesquisando usando aTKey
. Quando o único tipo de chave eraObject
, como foi o caso não genéricoOrderedDictionary
, o tipo de argumento passado para o indexador seria suficiente para distinguir se o tipo de operação de indexação deveria ser executada. No entanto, não está claro como o indexador de umOrderedDictionary<int, TValue>
deve se comportar.Se classes como
Drawing.Point
recomendaram e seguiram uma regra em que estruturas mutáveis por partes deveriam expor seus elementos mutáveis como campos, em vez de propriedades, e se absterem de usar setters de propriedades que modificamthis
, então umOrderedDictionary<TKey,TValue>
poderia expor com eficiência umaByIndex
propriedade que retornasse umaIndexer
estrutura que continha uma referência a o dicionário e tinha uma propriedade indexada cujo getter e setter chamariamGetByIndex
eSetByIndex
sobre ele. Assim, pode-se dizer algo comoMyDict.ByIndex[5] += 3;
adicionar 3 ao sexto elemento do dicionário.Infelizmente, para o compilador aceitar uma coisa dessas, seria necessário fazer com que a
ByIndex
propriedade retornasse uma nova instância de classe em vez de uma estrutura toda vez que for chamada, eliminando as vantagens que uma pessoa obteria ao evitar o boxe.No VB.NET, era possível contornar esse problema usando uma propriedade indexada nomeada (portanto,
MyDict.ByIndex[int]
seria um membro deMyDict
, em vez de exigirMyDict.ByIndex
que ele seja um membroMyDict
que inclui um indexador), mas o C # não permite tais coisas.Ainda poderia valer a pena oferecer um
OrderedDictionary<TKey,TValue> where TKey:class
, mas grande parte do motivo para fornecer genéricos em primeiro lugar era permitir o uso deles com tipos de valor.fonte
int
as chaves do tipo representam um desafio, mas isso pode ser evitado seguindo o exemplo doSortedList<TKey, TValue>
tipo relacionado : suporte chaves apenas com[...]
e requer o uso de.Values[...]
para acesso pelo índice. (SortedDictionary<TKey, TValue>
Em contraste, o que não está optimizado para o acesso indexada, requer o uso de.ElementAt(...).Value
)Para muitos propósitos, descobri que se pode sobreviver com um
List<KeyValuePair<K, V>>
. (Não se você precisar estenderDictionary
, obviamente, e não se precisar melhor do que a pesquisa de valor-chave O (n).)fonte
Tuple<T1, T2>
se eles não tiverem um relacionamento de valor-chave.Existe
SortedDictionary<TKey, TValue>
. Embora semanticamente próximo, não estou afirmando que é o mesmo queOrderedDictionary
simplesmente porque não é. Mesmo a partir de características de desempenho. No entanto, a diferença muito interessante e muito importante entreDictionary<TKey, TValue>
(e nessa extensãoOrderedDictionary
e implementações fornecidas nas respostas) eSortedDictionary
é que o último está usando a árvore binária por baixo. Essa é uma distinção crítica porque torna a classe imune a restrições de memória aplicadas à classe genérica. Veja este tópico sobreOutOfMemoryExceptions
lançada quando a classe genérica é usada para lidar com um grande conjunto de pares de valores-chave.Como descobrir o valor máximo para o parâmetro de capacidade passado ao construtor Dictionary para evitar OutOfMemoryException?
fonte
SortedDictionary
na ordem em que foram adicionados ? Isso é o queOrderedDictionary
permite. Conceito diferente do que classificado . (Eu cometi esse erro no passado; eu pensei que fornecer um construtor Comparer to OrderedDictionary o classificaria, mas tudo o que ele faz com o Comparer é determinar a igualdade de chaves; por exemplo, comparador insensível a cadeias permite a pesquisa de chaves insensíveis a cadeias.)Certo, é uma omissão infeliz. Sinto falta do OrderedDict do Python
Então, eu escrevi minha própria
OrderedDictionary<K,V>
classe em c #. Como funciona? Ele mantém duas coleções - um dicionário não ordenado de baunilha e uma lista ordenada de chaves. Com esta solução, as operações padrão de dicionário mantêm suas complexidades rápidas e a pesquisa por índice também é rápida.https://gist.github.com/hickford/5137384
Aqui está a interface
fonte
Como acompanhamento do comentário de @VB, aqui está uma implementação acessível do System.Runtime.Collections.OrderedDictionary <,> . Inicialmente, eu ia acessá-lo por reflexão e fornecê-lo através de uma fábrica, mas a dll em que está inserida não parece ser muito acessível, então apenas puxei a própria fonte.
Uma coisa a observar é que o indexador aqui não será lançado
KeyNotFoundException
. Eu absolutamente odeio essa convenção e essa foi a liberdade que tomei nesta implementação. Se isso é importante para você, basta substituir a linha porreturn default(TValue);
. Usa C # 6 ( compatível com o Visual Studio 2013 )Solicitações / discussões de recebimento aceitas no GitHub
fonte
Para quem procura uma opção de pacote "oficial" no NuGet, a implementação de um OrderedDictionary genérico foi aceita no .NET CoreFX Lab. Se tudo der certo, o tipo será eventualmente aprovado e integrado ao repositório principal do .NET CoreFX.
Existe a possibilidade de que essa implementação seja rejeitada.
A implementação confirmada pode ser referenciada aqui https://github.com/dotnet/corefxlab/blob/57be99a176421992e29009701a99a370983329a6/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/OrderedDictionary.cs
O pacote NuGet que definitivamente possui esse tipo disponível para uso pode ser encontrado aqui https://www.nuget.org/packages/Microsoft.Experimental.Collections/1.0.6-e190117-3
Ou você pode instalar o pacote no Visual Studio. Procure o pacote "Microsoft.Experimental.Collections" e verifique se a caixa de seleção "Incluir pré-lançamento" está marcada.
Atualizará esta postagem se e quando o tipo for disponibilizado oficialmente.
fonte
Eu implementei um genérico
OrderedDictionary<TKey, TValue>
, envolvendoSortedList<TKey, TValue>
e adicionando um privadoDictionary<TKey, int>
_order
. Então eu criei uma implementação interna deComparer<TKey>
, passando uma referência ao dicionário _order. Então eu uso esse comparador para o SortedList interno. Essa classe mantém a ordem dos elementos passados para o construtor e a ordem das adições.Essa implementação possui quase as mesmas características grandes de O,
SortedList<TKey, TValue>
já que a adição e remoção de _order é O (1). Cada elemento ocupará (de acordo com o livro 'C # 4 em poucas palavras', p. 292, tabela 7-1) um espaço de memória adicional de 22 (sobrecarga) + 4 (ordem int) + tamanho da chave (vamos assumir 8) = 34 Juntamente comSortedList<TKey, TValue>
a sobrecarga de dois bytes, a sobrecarga total é de 36 bytes, enquanto o mesmo livro diz que não genéricoOrderedDictionary
possui uma sobrecarga de 59 bytes.Se eu passar
sorted=true
para o construtor, o _order não é usado, o queOrderedDictionary<TKey, TValue>
é exatamenteSortedList<TKey, TValue>
com uma sobrecarga menor para o agrupamento, se é que tem algum significado.Vou armazenar não tantos objetos de referência grandes no
OrderedDictionary<TKey, TValue>
, então para mim este ca. A sobrecarga de 36 bytes é tolerável.O código principal está abaixo. O código completo atualizado está nesta essência .
fonte
OrderedDictionary
, com relação a inserções ou exclusões: (1) Nunca haverá exclusões; (2) haverá exclusões, mas o importante é que os itens sejam enumerados na ordem adicionada; não há necessidade de acesso por índice; (3) o índice numérico de um item deve (ou pelo menos pode) permanecer constante e não mais de 2 bilhões de itens serão adicionados durante a vida útil da coleção; portanto, se o item # 7 for removido, nunca mais haverá um item # 7; (4) o índice de um item deve ser sua classificação em relação aos sobreviventes.Dictionary
. Os cenários 2 e 3 podem ser gerenciados, mantendo cada item um índice informando quando foi adicionado e links para itens adicionados antes ou depois dele. O cenário 4 é o único que parece que não deveria conseguir o desempenho O (1) para operações em sequência arbitrária. Dependendo dos padrões de uso, o nº 4 pode ser ajudado usando várias estratégias de atualização lenta (manter as contagens em uma árvore e fazer com que as alterações em um nó sejam invalidadas em vez de atualizar o nó e seus pais).