Estou tentando fazer uma tabela de pesquisa de dicionário em C #. Eu preciso resolver uma 3-tupla de valores para uma string. Tentei usar arrays como chaves, mas não funcionou e não sei mais o que fazer. Neste ponto, estou pensando em fazer um Dicionário de Dicionários de Dicionários, mas provavelmente não seria muito bonito de se ver, embora seja como eu faria em javascript.
c#
dictionary
hashtable
tuples
AlexH
fonte
fonte
GetHashCode
implementação não é muito boa. É invariante sob a permutação dos campos.new object()
seria igual a outronew object()
? Ele não usa apenas referência direta comarison ... tente:bool test = new Tuple<int, string>(1, "foo").Equals(new Tuple<int, string>(1, "Foo".ToLower()));
Entre abordagens baseadas em tupla e dicionários aninhados, quase sempre é melhor ir para tupla.
Do ponto de vista da sustentabilidade ,
é muito mais fácil implementar uma funcionalidade semelhante a:
do que
do lado do receptor. No segundo caso, cada adição, pesquisa, remoção etc. requer ação em mais de um dicionário.
Além disso, se sua chave composta exigir um campo a mais (ou menos) no futuro, você precisará alterar muito o código no segundo caso (dicionário aninhado), uma vez que terá de adicionar outros dicionários aninhados e verificações subsequentes.
Do ponto de vista do desempenho , a melhor conclusão que você pode chegar é medindo você mesmo. Mas existem algumas limitações teóricas que você pode considerar de antemão:
No caso do dicionário aninhado, ter um dicionário adicional para cada chave (externa e interna) terá alguma sobrecarga de memória (mais do que a criação de uma tupla teria).
No caso do dicionário aninhado, todas as ações básicas como adição, atualização, pesquisa, remoção, etc., precisam ser realizadas em dois dicionários. Agora, há um caso em que a abordagem de dicionário aninhado pode ser mais rápida, ou seja, quando os dados pesquisados estão ausentes, uma vez que os dicionários intermediários podem ignorar o cálculo e comparação do código hash completo, mas, novamente, deve ser cronometrado para ter certeza. Na presença de dados, deve ser mais lento, pois as pesquisas devem ser realizadas duas vezes (ou três, dependendo do aninhamento).
Em relação à abordagem tupla, tuplas .NET não são os mais performance quando eles foram feitos para ser usado como chaves em conjuntos desde a sua
Equals
eGetHashCode
causas de implementação boxe para tipos de valor .Eu escolheria um dicionário baseado em tupla, mas se eu quiser mais desempenho, eu usaria minha própria tupla com melhor implementação.
Por outro lado, poucos cosméticos podem tornar o dicionário legal:
As chamadas do estilo do indexador podem ser muito mais claras e intuitivas. Por exemplo,
Portanto, exponha os indexadores necessários em sua classe de dicionário que lida internamente com as inserções e pesquisas.
Além disso, implemente uma
IEnumerable
interface adequada e forneça umAdd(TypeA, TypeB, TypeC, string)
método que forneça a sintaxe do inicializador de coleção, como:fonte
string foo = dict[a][b][c]
:?a
,. Você poderia apenas iterar qualquer dicionário como qualquer coleção normal e verificar a propriedade da chave, se estivera
. Se você sempre deseja obter o item em dict pela primeira propriedade, você pode projetar melhor o dicionário como um dicionário de dicionários, conforme mostrado em minha resposta e consulta comodict[a]
, o que lhe dá outro dicionário.4
para ambas as chavesa
eb
, então você pode torná-lo um dicionário padrão e adicionar valores comodict[a] = 4
edict[b] = 4
. Pode não fazer sentido se logicamente seua
eb
devesse ser uma unidade. Nesse caso, você pode definir um personalizadoIEqualityComparer
que iguale duas instâncias-chave como iguais se alguma de suas propriedades for igual. Tudo isso pode ser feito genericamente com reflexão.Se você estiver no C # 7, deve considerar o uso de tuplas de valor como sua chave composta. As tuplas de valor geralmente oferecem melhor desempenho do que as tuplas de referência tradicionais (
Tuple<T1, …>
), uma vez que as tuplas de valor são tipos de valor (structs), não tipos de referência, portanto, evitam os custos de alocação de memória e coleta de lixo. Além disso, eles oferecem uma sintaxe mais concisa e intuitiva, permitindo que seus campos sejam nomeados se você desejar. Eles também implementam aIEquatable<T>
interface necessária para o dicionário.fonte
As maneiras boas, limpas, rápidas, fáceis e legíveis são:
adicione algo semelhante a isto:
Então você pode usá-lo com o dicionário:
fonte
public TypeA DataA => Item1;
Se, por algum motivo, você realmente deseja evitar criar sua própria classe Tuple, ou usar um embutido no .NET 4.0, existe uma outra abordagem possível; você pode combinar os três valores-chave em um único valor.
Por exemplo, se os três valores forem tipos inteiros juntos não ocupando mais de 64 bits, você pode combiná-los em um
ulong
.Na pior das hipóteses, você sempre pode usar uma string, desde que certifique-se de que os três componentes nela são delimitados com algum caractere ou sequência que não ocorre dentro dos componentes da chave, por exemplo, com três números que você pode tentar:
Obviamente, há alguma sobrecarga de composição nessa abordagem, mas dependendo do que você está usando, isso pode ser trivial o suficiente para não se importar com isso.
fonte
JavaScriptSerializer
para concatenar uma matriz de tipos de string e / ou inteiros para você. Dessa forma, você não precisa criar um caractere delimitador sozinho.key1
,key2
,key3
) foram strings contendo o deliminator ("#"
)Eu substituiria sua Tupla por um GetHashCode apropriado e apenas o usaria como a chave.
Contanto que você sobrecarregue os métodos adequados, deverá ver um desempenho decente.
fonte
Aqui está a tupla do .NET para referência:
fonte
Se o seu código de consumo pode se contentar com uma interface IDictionary <>, em vez de Dictionary, meu instinto teria sido usar um SortedDictionary <> com um comparador de array personalizado, ou seja:
E crie assim (usando int [] apenas para um exemplo concreto):
fonte