Estruturas de dados .NET: ArrayList, List, HashTable, Dictionary, SortedList, SortedDictionary - Velocidade, memória e quando usar cada uma?


213

O .NET possui muitas estruturas de dados complexas. Infelizmente, alguns deles são bastante semelhantes, e nem sempre tenho certeza quando usar um e quando usar outro. A maioria dos meus livros em C # e Visual Basic fala sobre eles até certo ponto, mas eles nunca entram em detalhes reais.

Qual é a diferença entre Array, ArrayList, List, Hashtable, Dictionary, SortedList e SortedDictionary?

Quais são enumeráveis ​​(IList - pode executar loops 'foreach')? Quais usam pares de chave / valor (IDict)?

E quanto à pegada de memória? Velocidade de inserção? Velocidade de recuperação?

Existem outras estruturas de dados que vale a pena mencionar?

Ainda estou procurando mais detalhes sobre o uso e a velocidade da memória (notação Big-O).


12
Você deve separar esta questão. Você está perguntando vinte coisas diferentes, metade das quais uma simples pesquisa no Google pode responder. Por favor seja mais específico; é difícil ajudar quando sua pergunta está tão dispersa.

33
Pensei em terminar, mas percebi que alguém provavelmente seria capaz de consolidar todas essas respostas em um só lugar. De fato, se alguém puder criar uma tabela com o perfil de tudo, isso poderá se tornar um recurso maravilhoso neste site.
Pretzel

9
Esta pergunta pode ser transformada em um wiki?
precisa saber é o seguinte

1
Este artigo MSDN cobre muitas destas questões, incluindo árvores, gráficos e conjuntos, Um exame extensivo de Estruturas de Dados
Ryan Fisher

1
Ryan, os artigos nesse link têm 14 anos (12 na época da publicação). Nota lateral: Eu os tenho lido há uma semana. mas eles também não incluem novas tecnologias e precisam desesperadamente de atualização. E mais métricas e exemplos de desempenho.
Htm11h 17/05

Respostas:


156

Em cima da minha cabeça:

  • Array* - representa uma matriz de memória antiga - como um apelido para uma type[]matriz normal . Pode enumerar. Não é possível crescer automaticamente. Eu assumiria inserção muito rápida e velocidade de recuperação.

  • ArrayList- matriz que cresce automaticamente. Adiciona mais sobrecarga. Pode enum., Provavelmente mais lento que um array normal, mas ainda bem rápido. Eles são muito usados ​​no .NET

  • List- um dos meus favoritos - pode ser usado com genéricos, para que você possa ter uma matriz fortemente tipada, por exemplo List<string>. Fora isso, age muito parecido comArrayList

  • Hashtable- hashtable antigo simples. O (1) a O (n) pior caso. Pode enumerar as propriedades value e keys e fazer pares key / val

  • Dictionary - o mesmo que acima, apenas fortemente tipado via genéricos, como Dictionary<string, string>

  • SortedList- uma lista genérica classificada. Diminuiu a velocidade da inserção, pois precisa descobrir onde colocar as coisas. Pode enum., Provavelmente o mesmo na recuperação, pois não precisa recorrer, mas a exclusão será mais lenta que uma lista antiga simples.

Eu costumo usar Liste Dictionaryo tempo todo - uma vez que você começa a usá-los fortemente digitados com genéricos, é realmente difícil voltar aos não-genéricos padrão.

Também existem muitas outras estruturas de dados - existem as KeyValuePairque você pode usar para fazer algumas coisas interessantes, e outras SortedDictionaryque também podem ser úteis.


3
Tabela Hash é O (1), o pior caso (com colisões) pode ser O (n)
Justin Bozonier

7
Existem muitas outras estruturas de dados que você precisa adicionar aqui. como LinkedList, Skip List, Stack, Queue, Heap, Trees, Graphs. Essas são estruturas de dados muito importantes também.
DarthVader

2
ConcurrentDictionary adicionado no .Net 4.0 fornece um dicionário genérico com Thread Safety
Harindaka

2
O BlockingCollection <T> também fornece uma implementação segura de produtor / consumidor de
thread

7
ArrayListusa métodos virtuais, mas List<T>não. ArrayListfoi amplamente substituído List<T>por coleções padrão e Collection<T>como uma classe base para coleções personalizadas. Hashtablefoi amplamente substituído por Dictionary<TKey, TValue>. Eu recomendaria evitar ArrayListe Hashtablepara o novo código.
Sam Harwell 23/05

29

Se possível, use genéricos. Isso inclui:

  • Lista em vez de ArrayList
  • Dicionário em vez de HashTable

24

Primeiro, todas as coleções no .NET implementam IEnumerable.

Segundo, muitas das coleções são duplicadas porque os genéricos foram adicionados na versão 2.0 da estrutura.

Portanto, embora as coleções genéricas provavelmente adicionem recursos, na maioria das vezes:

  • List é uma implementação genérica de ArrayList.
  • Dictionary é uma implementação genérica do Hashtable

Matrizes são uma coleção de tamanho fixo que você pode alterar o valor armazenado em um determinado índice.

SortedDictionary é um IDictionary que é classificado com base nas chaves. SortedList é um IDictionary que é classificado com base em um IComparer necessário.

Portanto, as implementações do IDictionary (aquelas que suportam KeyValuePairs) são: * Hashtable * Dictionary * SortedList * SortedDictionary

Outra coleção que foi adicionada no .NET 3.5 é o Hashset. É uma coleção que suporta operações definidas.

Além disso, o LinkedList é uma implementação de lista vinculada padrão (a Lista é uma lista de matriz para recuperação mais rápida).


20

Aqui estão algumas dicas gerais para você:

  • Você pode usar foreachem tipos que implementam IEnumerable. IListé essencialmente uma propriedade IEnumberablewith Counte Item(acessando itens usando um índice baseado em zero). IDictionarypor outro lado, significa que você pode acessar itens por qualquer índice hashável.

  • Array, ArrayListE Listtodos os implementar IList. Dictionary, SortedDictionarye Hashtableimplemente IDictionary.

  • Se você estiver usando o .NET 2.0 ou superior, é recomendável usar equivalentes genéricos dos tipos mencionados.

  • Para complexidade de tempo e espaço de várias operações nesses tipos, você deve consultar a documentação deles.

  • As estruturas de dados .NET estão no System.Collectionsespaço para nome. Existem bibliotecas de tipos, como o PowerCollections, que oferecem estruturas de dados adicionais.

  • Para obter um entendimento completo das estruturas de dados, consulte recursos como o CLRS .


1
da MSDN , parece que SortedList implementar IDictionnary - não IList
Haim Bendanan

Fixo. obrigado pelo comentário. Parece que SortedList mantém uma lista de chaves / valores, representando basicamente os dados de um dicionário. Não me lembro como esta classe funcionou quando escrevi pela primeira vez a resposta ...
BlackWing

9

Estruturas de dados .NET:

Mais informações sobre por que ArrayList e List são realmente diferentes

Matrizes

Como um usuário declara, matrizes são a coleção "old school" (sim, matrizes são consideradas uma coleção, embora não façam parte dela System.Collections). Mas, o que é "old school" sobre matrizes em comparação com outras coleções, ou seja, aquelas que você listou em seu título (aqui, ArrayList e List (Of T))? Vamos começar com o básico, olhando para matrizes.

Para começar, as matrizes no Microsoft .NET são "mecanismos que permitem tratar vários itens [relacionados à lógica] como uma única coleção" (consulte o artigo vinculado). O que isso significa? As matrizes armazenam membros individuais (elementos) sequencialmente, um após o outro na memória com um endereço inicial. Usando a matriz, podemos acessar facilmente os elementos armazenados em sequência, começando nesse endereço.

Além disso, e ao contrário de programar 101 concepções comuns, as matrizes realmente podem ser bastante complexas:

As matrizes podem ser de dimensão única, multidimensionais ou jadded (vale a pena ler sobre matrizes irregulares). As matrizes em si não são dinâmicas: uma vez inicializadas, uma matriz de n tamanho reserva espaço suficiente para armazenar n número de objetos. O número de elementos na matriz não pode aumentar ou diminuir. Dim _array As Int32() = New Int32(100)reserva espaço suficiente no bloco de memória para que a matriz contenha 100 objetos do tipo primitivo Int32 (nesse caso, a matriz é inicializada para conter 0s). O endereço desse bloco é retornado para _array.

De acordo com o artigo, o Common Language Specification (CLS) exige que todas as matrizes sejam baseadas em zero. Matrizes no .NET oferecem suporte a matrizes não baseadas em zero; no entanto, isso é menos comum. Como resultado do "comum" de matrizes baseadas em zero, a Microsoft passou muito tempo otimizando seu desempenho ; portanto, matrizes de dimensão única, baseadas em zero (SZs) são "especiais" - e realmente a melhor implementação de uma matriz (ao contrário de multidimensionais etc.) - porque as SZs têm instruções específicas de linguagem intermediária para manipulá-las.

As matrizes são sempre passadas por referência (como um endereço de memória) - uma peça importante do quebra-cabeça da matriz a ser conhecida. Enquanto eles fazem a verificação de limites (gerará um erro), a verificação de limites também pode ser desabilitada nas matrizes.

Novamente, o maior obstáculo às matrizes é que elas não são redimensionáveis. Eles têm uma capacidade "fixa". Apresentando ArrayList e List (Of T) à nossa história:

ArrayList - lista não genérica

O ArrayList (junto com List(Of T)- embora haja algumas diferenças críticas, aqui explicadas mais adiante) - talvez seja melhor pensado como a próxima adição às coleções (no sentido amplo). ArrayList herda da interface IList (um descendente de 'ICollection'). As próprias ArrayLists são mais volumosas - exigindo mais sobrecarga - do que as Listas.

IListpermite que a implementação trate ArrayLists como listas de tamanho fixo (como Arrays); no entanto, além da funcionalidade adicional adicionada por ArrayLists, não há vantagens reais em usar ArrayLists de tamanho fixo, pois ArrayLists (sobre Arrays) nesse caso são marcadamente mais lentas.

Na minha leitura, ArrayLists não pode ser irregular: "O uso de matrizes multidimensionais como elementos ... não é suportado". Mais uma vez, outro prego no caixão de ArrayLists. ArrayLists também não são "digitado" - o que significa que, por baixo de tudo, um ArrayList é simplesmente uma matriz dinâmica de objetos: Object[]. Isso requer muito boxe (implícito) e unboxing (explícito) ao implementar ArrayLists, aumentando novamente sua sobrecarga.

Pensamento sem fundamento: acho que lembro de ter lido ou ouvido falar de um de meus professores que ArrayLists são uma espécie de filho conceitual bastardo da tentativa de passar de Arrays para Coleções do tipo Lista, ou seja, embora tenha sido uma grande melhoria para Arrays, eles não são mais a melhor opção, pois um maior desenvolvimento foi feito com relação às coleções

Lista (de T): O que ArrayList se tornou (e esperava ser)

A diferença no uso da memória é significativa o suficiente para onde uma Lista (Of Int32) consumiu 56% menos memória que uma ArrayList contendo o mesmo tipo primitivo (8 MB vs. 19 MB na demonstração vinculada do cavalheiro acima: novamente, vinculada aqui ) - embora este é um resultado composto pela máquina de 64 bits. Essa diferença realmente demonstra duas coisas: primeiro (1), um "objeto" do tipo Int32 (ArrayList) é muito maior que um tipo primitivo do Int32 puro (List); segundo (2), a diferença é exponencial como resultado do funcionamento interno de uma máquina de 64 bits.

Então, qual é a diferença e o que é uma lista (de T) ? O MSDN define List(Of T)como, "... uma lista fortemente digitada de objetos que podem ser acessados ​​pelo índice". A importância aqui é o bit "fortemente tipado": uma Lista (de T) 'reconhece' tipos e armazena os objetos como seu tipo. Portanto, um Int32é armazenado como Int32e não como um Objecttipo. Isso elimina os problemas causados ​​pelo boxe e unboxing.

O MSDN especifica que essa diferença só entra em jogo ao armazenar tipos primitivos e não tipos de referência. Além disso, a diferença realmente ocorre em grande escala: mais de 500 elementos. O mais interessante é que a documentação do MSDN diz: "É da sua vantagem usar a implementação específica do tipo da classe List (Of T) em vez de usar a classe ArrayList ...."

Essencialmente, List (Of T) é ArrayList, mas melhor. É o "equivalente genérico" de ArrayList. Como ArrayList, não é garantido que seja classificado até ser classificado (veja figura). A lista (Of T) também possui algumas funcionalidades adicionais.


5

Eu simpatizo com a pergunta - eu também achei (achar?) A escolha desconcertante, então decidi cientificamente ver qual estrutura de dados é a mais rápida (fiz o teste usando VB, mas imagino que C # seria o mesmo, pois os dois idiomas faça a mesma coisa no nível CLR). Você pode ver alguns resultados de benchmarking realizados por mim aqui (também há uma discussão sobre qual tipo de dados é melhor usar em quais circunstâncias).


3

Eles estão bem explicados no intellisense. Basta digitar System.Collections. ou System.Collections.Generics (preferencial) e você obterá uma lista e uma breve descrição do que está disponível.


3

Hashtables / Dictionaries são desempenho O (1), o que significa que o desempenho não é uma função do tamanho. Isso é importante saber.

EDIT: Na prática, a complexidade média do tempo para pesquisas Hashtable / Dictionary <> é O (1).


5
Não existe "desempenho". A complexidade depende da operação. Por exemplo, se você inserir n elementos no Dicionário <>, ele não será O (1) devido a uma nova verificação.
Ilya Ryzhenkov

2
Para sua informação, mesmo com a atualização, o Dicionário ainda é O (1). Considere o cenário imediatamente antes da expansão do dicionário. Metade dos elementos - aqueles que foram adicionados desde a última expansão - terá um hash uma vez. Metade do restante terá hash duas vezes. Metade do restante, três vezes, etc. O número médio de operações de hash executadas em cada elemento será 1 + 1/2 + 1/4 + 1/8 ... = 2. A situação imediatamente após a expansão é essencialmente a mesma, mas com cada elemento tendo sido hashizado um tempo extra (a contagem média de hash é três). Todos os outros cenários estão entre eles.
22711

3

As coleções genéricas terão um desempenho melhor do que suas contrapartes não genéricas, especialmente ao iterar por vários itens. Isso ocorre porque o boxe e o unboxing não ocorrem mais.


2

Uma observação importante sobre o Hashtable vs Dictionary para engenharia de negociação sistemática de alta frequência: Problema de segurança do thread

Hashtable é thread safe para uso por vários threads. Os membros estáticos públicos do dicionário são seguros para threads, mas não é garantido que qualquer membro da instância.

Portanto, o Hashtable continua sendo a escolha "padrão" a esse respeito.


Isto é parcialmente verdade. A Hashtableé seguro para uso com apenas um escritor e vários leitores simultaneamente. Por outro lado, é seguro usá-lo Dictionarycom vários leitores, desde que não seja modificado simultaneamente.
Bryan Menard

Definitivamente. No espaço de negociação, no entanto, estamos lendo simultaneamente dados de mercado ao vivo e executando análises que incluem as entradas anexas. Também depende de quantos traders estão utilizando o sistema - se for apenas você, obviamente não importa.
28411 Rob

1
O .NET 4.0 fornece um ConcurrentDictionary <TKey, TValue>
Rob

1

Existem diferenças sutis e não tão sutis entre coleções genéricas e não genéricas. Eles simplesmente usam diferentes estruturas de dados subjacentes. Por exemplo, o Hashtable garante um escritor para muitos leitores sem sincronização. Dicionário não.


1

Estruturas e coleções de dados em C # mais populares

  • Matriz
  • ArrayList
  • Lista
  • LinkedList
  • Dicionário
  • HashSet
  • Pilha
  • Fila
  • SortedList

O C # .NET possui várias estruturas de dados diferentes, por exemplo, uma das mais comuns é uma matriz. No entanto, o C # vem com muitas estruturas de dados mais básicas. A escolha da estrutura de dados correta a ser usada faz parte da criação de um programa bem estruturado e eficiente.

Neste artigo, examinarei as estruturas de dados C # internas, incluindo as novas introduzidas no C # .NET 3.5. Observe que muitas dessas estruturas de dados se aplicam a outras linguagens de programação.

Matriz

A estrutura de dados talvez mais simples e mais comum é a matriz. A matriz AC # é basicamente uma lista de objetos. Suas características definidoras são que todos os objetos são do mesmo tipo (na maioria dos casos) e há um número específico deles. A natureza de uma matriz permite um acesso muito rápido aos elementos com base em sua posição na lista (também conhecida como índice). A matriz AC # é definida assim:

[object type][] myArray = new [object type][number of elements]

Alguns exemplos:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Como você pode ver no exemplo acima, uma matriz pode ser inicializada sem elementos ou com um conjunto de valores existentes. Inserir valores em uma matriz é simples, desde que eles se ajustem. A operação se torna cara quando há mais elementos que o tamanho da matriz, momento em que a matriz precisa ser expandida. Isso leva mais tempo porque todos os elementos existentes devem ser copiados para a nova matriz maior.

ArrayList

A estrutura de dados do C #, ArrayList, é uma matriz dinâmica. O que isso significa é que um ArrayList pode ter qualquer quantidade de objetos e de qualquer tipo. Essa estrutura de dados foi projetada para simplificar os processos de adição de novos elementos em uma matriz. Sob o capô, um ArrayList é um array cujo tamanho é dobrado toda vez que fica sem espaço. Dobrar o tamanho da matriz interna é uma estratégia muito eficaz que reduz a quantidade de cópias de elementos a longo prazo. Não vamos entrar na prova disso aqui. A estrutura de dados é muito simples de usar:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

A desvantagem da estrutura de dados ArrayList é que você deve converter os valores recuperados novamente em seu tipo original:

int arrayListValue = (int)myArrayList[0]

Fontes e mais informações, você pode encontrar aqui :


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.