Eu já vi algumas maneiras diferentes de percorrer um dicionário em c #. Existe uma maneira padrão?
c#
dictionary
loops
Jake Stewart
fonte
fonte
Respostas:
fonte
var entry
é melhor nesse caso e, portanto, votei esta resposta em uma segunda olhada em vez da acima.var
quando você não sabe o tipo geralmente é uma prática ruim.var
só funciona se o tipo for conhecido em tempo de compilação. Se o Visual Studio souber o tipo, também estará disponível para você descobrir.Se você estiver tentando usar um dicionário genérico em C #, como usaria uma matriz associativa em outro idioma:
Ou, se você só precisar iterar sobre a coleção de chaves, use
E, por último, se você estiver interessado apenas nos valores:
(Observe que a
var
palavra-chave é um recurso opcional do C # 3.0 e acima; você também pode usar o tipo exato de suas chaves / valores aqui)fonte
myDictionary
(a menos que esse seja o nome real, é claro). Acho que usando var é bom quando o tipo é por exemplo óbviovar x = "some string"
, mas quando não é imediatamente óbvio que eu acho que é preguiçoso codificação que fere o leitor de código / revisorvar
deve ser usado com moderação, na minha opinião. Particularmente aqui, não é construtivo: o tipoKeyValuePair
é provavelmente relevante para a questão.var
tem um propósito único e não acredito que seja um açúcar "sintático". Usá-lo propositadamente é uma abordagem apropriada.Em alguns casos, você pode precisar de um contador que pode ser fornecido pela implementação de loop for. Para isso, o LINQ fornece o
ElementAt
que permite o seguinte:fonte
ElementAt
uma operação O (n)?.ElementAt
nesse contexto pode levar a erros sutis. Muito mais sério é o argumento de Arturo acima. Você repetirá osdictionary.Count + 1
tempos do dicionário que levam à complexidade de O (n ^ 2) para uma operação que deve ser apenas O (n). Se você realmente precisa de um índice (se precisar, provavelmente está usando o tipo de coleção errado), deve iterardictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
e não usar.ElementAt
dentro do loop.Depende se você está atrás das chaves ou dos valores ...
Na
Dictionary(TKey, TValue)
descrição da classe MSDN :fonte
Geralmente, pedir "o melhor caminho" sem um contexto específico é como perguntar qual é a melhor cor ?
Por um lado, há muitas cores e não há melhor cor. Depende da necessidade e, muitas vezes, do gosto também.
Por outro lado, existem muitas maneiras de iterar um Dicionário em C # e não há a melhor maneira. Depende da necessidade e, muitas vezes, do gosto também.
Maneira mais direta
Se você precisar apenas do valor (permite chamá-lo
item
, mais legível quekvp.Value
).Se você precisar de uma ordem de classificação específica
Geralmente, os iniciantes ficam surpresos com a ordem de enumeração de um dicionário.
O LINQ fornece uma sintaxe concisa que permite especificar a ordem (e muitas outras coisas), por exemplo:
Novamente, você pode precisar apenas do valor. O LINQ também fornece uma solução concisa para:
item
, mais legível quekvp.Value
)Aqui está:
Existem muitos outros casos de uso do mundo real que você pode fazer com esses exemplos. Se você não precisa de um pedido específico, siga o "caminho mais direto" (veja acima)!
fonte
.Values
e não uma cláusula de seleção.Value
campo. O tipo exato que vejo aqui éIOrderedEnumerable<KeyValuePair<TKey, TValue>>
. Talvez você quis dizer outra coisa? Você pode escrever uma linha completa mostrando o que você quer dizer (e testá-lo)?items.Value
como você sugeriu. No caso da quarta seção que você comentou,Select()
é uma maneira de fazerforeach
enumerar diretamente os valores no dicionário, em vez de pares de valores-chave. Se, de alguma forma, você não gostar doSelect()
caso, talvez prefira a terceira seção de código. O objetivo da quarta seção é mostrar que é possível pré-processar a coleção com o LINQ..Keys.Orderby()
isso, iterará em uma lista de chaves. Se é tudo o que você precisa, tudo bem. Se você precisar de valores, no loop, você precisará consultar o dicionário em cada chave para obter o valor. Em muitos cenários, não fará diferença prática. No cenário de alto desempenho, será. Como escrevi no começo da resposta: "existem muitas maneiras (...) e não há melhor maneira. Depende da necessidade e, muitas vezes, do gosto também".Eu diria que foreach é o caminho padrão, embora obviamente dependa do que você está procurando
É isso que você está procurando?
fonte
kvp
é comumente utilizado para nomear casos KeyValuePair quando iteração sobre dicionários e estruturas de dados relacionados:foreach(var kvp in myDictionary){...
.Você também pode tentar isso em grandes dicionários para processamento multithread.
fonte
O C # 7.0 introduziu desconstrutores e, se você estiver usando o .NET Core 2.0+ Application, a estrutura
KeyValuePair<>
já incluirá umDeconstruct()
para você. Então você pode fazer:fonte
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
Agradeço que esta pergunta já tenha recebido muitas respostas, mas queria lançar uma pequena pesquisa.
A iteração em um dicionário pode ser bastante lenta quando comparada à iteração em algo como uma matriz. Nos meus testes, uma iteração em uma matriz levou 0,015003 segundos, enquanto uma iteração em um dicionário (com o mesmo número de elementos) levou 0,0365073 segundos, 2,4 vezes mais! Embora eu tenha visto diferenças muito maiores. Para comparação, uma lista estava em algum lugar entre 0,00215043 segundos.
No entanto, é como comparar maçãs e laranjas. Meu argumento é que a iteração nos dicionários é lenta.
Os dicionários são otimizados para pesquisas, portanto, com isso em mente, criei dois métodos. Um simplesmente faz um foreach, o outro itera as teclas e depois olha para cima.
Este carrega as chaves e itera sobre elas (tentei puxar as chaves em uma string [], mas a diferença era insignificante.
Neste exemplo, o teste foreach normal levou 0,0310062 e a versão das chaves 0,2205441. Carregar todas as chaves e iterar em todas as pesquisas é claramente muito mais lento!
Para um teste final, eu executei minha iteração dez vezes para ver se há algum benefício em usar as chaves aqui (a essa altura eu estava curioso):
Aqui está o método RunTest, se isso ajudar você a visualizar o que está acontecendo.
Aqui, a execução foreach normal levou 0,2820564 segundos (cerca de dez vezes mais do que uma única iteração levou - como seria de esperar). A iteração sobre as chaves levou 2,2249449 segundos.
Editado para adicionar: a leitura de algumas das outras respostas me fez questionar o que aconteceria se eu usasse o Dictionary em vez do Dictionary. Neste exemplo, a matriz levou 0,0120024 segundos, a lista 0,0185037 segundos e o dicionário 0,0465093 segundos. É razoável esperar que o tipo de dados faça a diferença na velocidade do dicionário.
Quais são as minhas conclusões ?
fonte
Há muitas opções. Meu favorito pessoal é de KeyValuePair
Você também pode usar as coleções de chaves e valores
fonte
Com
.NET Framework 4.7
um pode usar a decomposiçãoPara fazer esse código funcionar em versões C # inferiores, adicione
System.ValueTuple NuGet package
e escreva em algum lugarfonte
ValueTuple
incorporado. Está disponível como um pacote nuget para versões anteriores. Mais importante, o C # 7.0+ é necessário para que oDeconstruct
método funcione como um desconstrutorvar (fruit, number) in fruits
.A partir do C # 7, você pode desconstruir objetos em variáveis. Acredito que esta seja a melhor maneira de iterar sobre um dicionário.
Exemplo:
Crie um método de extensão
KeyValuePair<TKey, TVal>
que o desconstrua:Itere qualquer um
Dictionary<TKey, TVal>
da seguinte maneirafonte
Você sugeriu abaixo para iterar
Para sua informação,
foreach
não funciona se o valor for do tipo objeto.fonte
foreach
não funcionará se qual valor é do tipoobject
? Caso contrário, isso não faz muito sentido.Forma mais simples para iterar um dicionário:
fonte
Usando o C # 7 , adicione este método de extensão a qualquer projeto da sua solução:
E use esta sintaxe simples
Ou este, se você preferir
No lugar do tradicional
O método de extensão transforma o
KeyValuePair
seuIDictionary<TKey, TValue>
em um tipo fortemente tipadotuple
, permitindo que você use essa nova sintaxe confortável.Ele converte - apenas - as entradas de dicionário necessárias em
tuples
, portanto, NÃO converte o dicionário inteiro emtuples
, para que não haja problemas de desempenho relacionados a isso.Existe apenas um custo menor chamando o método de extensão para criar uma
tuple
comparação com o usoKeyValuePair
direto, o que NÃO deve ser um problema se você estiver atribuindo asKeyValuePair
propriedadesKey
e propriedadesValue
para novas variáveis de loop de qualquer maneira.Na prática, essa nova sintaxe se adapta muito bem à maioria dos casos, exceto em cenários de baixo desempenho e alto desempenho, onde você ainda tem a opção de simplesmente não usá-la nesse local específico.
Confira: Blog do MSDN - Novos recursos no C # 7
fonte
kvp.Key
ekvp.Value
usar respectivamente a chave e o valor. Com as tuplas, você tem a flexibilidade de nomear a chave e o valor conforme desejar, sem usar mais declarações de variáveis dentro do bloco foreach. Por exemplo, você pode nomear sua chave comofactoryName
, e o valor comomodels
, o que é especialmente útil quando você obtém loops aninhados (dicionários de dicionários): a manutenção do código fica muito mais fácil. Apenas tente! ;-)Sei que essa é uma pergunta muito antiga, mas criei alguns métodos de extensão que podem ser úteis:
Dessa forma, eu posso escrever código como este:
fonte
Às vezes, se você só precisa que os valores sejam enumerados, use a coleção de valores do dicionário:
Relatado por este post que afirma que é o método mais rápido: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html
fonte
Encontrei esse método na documentação da classe DictionaryBase no MSDN:
Esse foi o único que eu consegui funcionar corretamente em uma classe herdada do DictionaryBase.
fonte
Hashtable
objetosforeach
é mais rápido e se você iterar apenas___.Values
, também é mais rápidofonte
ContainsKey()
nafor
versão? Isso adiciona uma sobrecarga extra que não está presente no código com o qual você está comparando.TryGetValue()
existe para substituir o padrão exato "se houver chave, obtenha item com chave". Além disso, sedict
contiver um intervalo contíguo de números inteiros de0
atédictCount - 1
, você sabe que o indexador não pode falhar; caso contrário,dict.Keys
é o que você deve estar iterando. De qualquer maneira, nãoContainsKey()
/TryGetValue()
necessário. Por fim, não poste capturas de tela do código.Vou aproveitar o .NET 4.0+ e fornecer uma resposta atualizada para a originalmente aceita:
fonte
A maneira padrão de iterar em um dicionário, de acordo com a documentação oficial no MSDN é:
fonte
Eu escrevi uma extensão para fazer um loop sobre um dicionário.
Então você pode ligar
fonte
ForEach
método no qual você temforeach (...) { }
... Parece desnecessário.Por exemplo, você deseja iterar sobre a coleção de valores por padrão, acredito que você possa implementar IEnumerable <>, onde T é o tipo do objeto de valores no dicionário e "this" é um dicionário.
fonte
Só queria adicionar meus 2 centavos, pois a maioria das respostas está relacionada ao loop foreach. Por favor, dê uma olhada no seguinte código:
Embora isso adicione uma chamada adicional de '.ToList ()', pode haver uma ligeira melhoria no desempenho (conforme indicado aqui para foreach vs someList.Foreach () {} ), espacialmente ao trabalhar com dicionários grandes e executar em paralelo. opção / não terá efeito algum.
Além disso, observe que você não poderá atribuir valores à propriedade 'Value' dentro de um loop foreach. Por outro lado, você também poderá manipular a 'Chave', possivelmente causando problemas em tempo de execução.
Quando você quiser apenas "ler" Chaves e Valores, também poderá usar IEnumerable.Select ().
fonte
foreach
força a visibilidade do efeito colateral para cima, onde ele pertence.Como já apontado nesta resposta ,
KeyValuePair<TKey, TValue>
implementa umDeconstruct
método que começa no .NET Core 2.0, .NET Standard 2.1 e .NET Framework 5.0 (visualização).Com isso, é possível iterar através de um dicionário de uma
KeyValuePair
maneira agnóstica:fonte
fonte
AggregateObject
adiciona aKeyValuePair
? Onde está a "iteração", conforme solicitado na pergunta?foreach
, mas eu usei muito. Minha resposta realmente merece voto negativo?Select
usa a iteração para efetuar o resultado, mas não é um iterador em si. Os tipos de itens para os quais a iteração (foreach
) é usada - especialmente operações com efeitos colaterais - estão fora do escopo do Linq, inclusiveSelect
. O lambda não será executado até queaggregateObjectCollection
seja realmente enumerado. Se essa resposta for tomada como um "primeiro caminho" (isto é, usado antes de uma retaforeach
), incentivará as práticas inadequadas. Situacionalmente, pode haver operações do Linq que sejam úteis antes da iteração de um dicionário, mas que não atendam à pergunta conforme solicitado.Dictionary <TKey, TValue> É uma classe de coleção genérica em c # e armazena os dados no formato do valor da chave. A chave deve ser única e não pode ser nula, enquanto o valor pode ser duplicado e nulo. Como cada item do dicionário é tratado como KeyValuePair <TKey, TValue> estrutura que representa uma chave e seu valor. e, portanto, devemos usar o tipo de elemento KeyValuePair <TKey, TValue> durante a iteração do elemento. Abaixo está o exemplo.
fonte
Se você deseja usar o loop for, você pode fazer isso:
fonte
foreach
loop e pior desempenho, porquenew List<string>(dictionary.Keys)
iterarádictionary.Count
vezes antes que você tenha a chance de iterá-lo. Deixando de lado que pedir "o melhor caminho" é subjetivo, não vejo como isso se qualificaria como "o melhor caminho" ou "caminho padrão" que a pergunta busca. Para "Se você deseja usar o loop ...", eu diria " Não use umfor
loop".foreach (var pair in dictionary.ToArray()) { }
. Ainda assim, acho que seria bom deixar claro na resposta o (s) cenário (s) específico (s) em que alguém gostaria de usar esse código e as implicações de fazê-lo.simples com linq
fonte
ToList()
porqueForEach()
é definido apenas naList<>
classe, mas por que tudo isso em vez de apenasforeach (var pair in dict) { }
? Eu diria que é ainda mais simples e não tem as mesmas implicações de memória / desempenho. Esta solução exata já foi proposta nesta resposta de 3,5 anos atrás, de qualquer maneira.além das postagens mais altas, onde há uma discussão entre usar
ou
o mais completo é o seguinte, porque você pode ver o tipo de dicionário na inicialização, kvp é KeyValuePair
fonte