Quando o vetor / lista deve ser usado?

17

Consigo entender quando usar listas, mas não entendo quando é melhor usar vetores do que usar listas em videogames: quando é melhor ter acesso aleatório rápido?

(E eu entendo por que é mais rápido inserir / excluir em listas porque apenas remove / adiciona ponteiros, mas ainda precisa encontrar o item correspondente ...)

jokoon
fonte
Adicione novamente a tag de vetor a isso - se a lista for uma tag válida, o vetor também será.
Kylotan
Presumivelmente, o vetor foi removido porque costumava significar vetor matemático, não std :: vector.
1
que tal nix-los e colocar container.
Deft_code
@Kylotan: É como Joe disse. Essa pergunta definitivamente era sobre vetores, mas não pertencia à tag de vetor.
Doppelgreener
1
Então, removemos alguma tag ambígua? Parece a decisão errada para mim - é melhor que sua pesquisa gere muitas informações do que insuficientes. Ignorar resultados indesejados é mais fácil do que fazer um brainstorming para encontrar sinônimos.
Kylotan

Respostas:

29

Minha regra de ouro, e tenho certeza de que haverá debate sobre isso, é nunca usar listas (a menos que você precise remover com muita, muita frequência coisas do meio de grandes listas).

A velocidade que você ganhará ao ter todos os seus elementos em seu contêiner na memória contígua (e, portanto, mais amigável ao cache) vale a compensação dos custos adicionais de adicionar / remover / redimensionar o vetor.

Edit: Apenas para esclarecer um pouco mais, é claro que não é preciso dizer que qualquer tipo de pergunta "que é mais rápida" deve ser testada em qualquer plataforma com quaisquer conjuntos de dados pertinentes às suas necessidades particulares. Se eu apenas precisar de uma coleção de elementos, apenas uso vetor (ou deque, que é quase a mesma coisa), a menos que haja um bom motivo para não fazê -lo.

Tetrad
fonte
1
Eu acho que depende muito das suas necessidades, se você nunca precisar acessar um elemento específico e apenas ler todos eles e adicionar e remover elementos com frequência, a lista é uma solução melhor.
Frédérick Imbeault 27/10/10
11
@ Frédérick: Essa é a sabedoria C ++ padrão, mas também quase sempre está errada. Os vetores, especialmente ao lidar com vetores de ponteiros (que você quase sempre é para jogos), são extremamente rápidos para remover coisas do meio - é tempo linear, mas é uma sobrecarga muito pequena por item. Também é muito mais rápido iterar sequencialmente sobre um vetor.
1
Não tenho certeza de que tipo de estrutura você está obtendo exatamente - esse tipo de otimização requer exemplos concretos para dizer qualquer coisa definitivamente. Por exemplo, minha primeira reação ao seu caso de uso seria um conjunto não ordenado, que permite inserção, exclusão e pesquisa igualmente rápidas; na minha experiência, os conjuntos são ótimos para objetos em editores. Porém, como os editores têm muitos requisitos de desempenho em tempo real mais flexíveis - por exemplo, é bom se responder a um pressionamento de botão "Excluir" demorar 1/20 de segundo ou 1/2 de segundo 10% do tempo - esse nível de otimização também raramente se aplica a eles.
1
@ Frédérick Imbeault Modified não é um grande problema, é Add \ Remove que causa problemas. Do ponto de adição / remoção até o final do vetor é copiado para manter o vetor contíguo. Se a ordem dos elementos não importa, você pode trocar o elemento excluído pelo último elemento e, em seguida, pop-lo para excluir e adicionar ao final para adicionar.
stonemetal
4
Certamente, pegar o endereço de um elemento em um vetor e armazená-lo não é seguro. Mas, de um modo geral, você quase nunca faz isso, preferindo ter um vetor de ponteiros de elementos e copiando o ponteiro ao redor (ou algo semelhante).
Tetrad
8

Use uma lista quando a invalidação do iterador causada pela modificação do meio da estrutura de dados causar um problema ou você precisar manter seus elementos classificados para que o truque de troca e pop para exclusões rápidas da coleção do meio não funcione e você tenha uma grande número de exclusões de coleção intermediária.

Você também pode considerar usar um Deque. Possui características de desempenho semelhantes às de um vetor, mas não possui necessidade de memória contígua e é um pouco mais flexível.

pedregoso
fonte
+1 por ser a única pessoa a mencionar deques - você obtém os benefícios contíguos de memória e velocidade de pesquisa de vetores, com inserção / remoção rápida nas duas extremidades.
5

Sua escolha deve refletir suas necessidades. Todos os elementos dos vetores são contínuos na memória e as listas têm ponteiros para os elementos seguintes / anteriores, para que cada um tenha suas vantagens / desvantagens:

Listas:

  • Cada elemento leva 2 números inteiros para apontar os elementos anteriores e seguintes, portanto, mais comumente, são 8 bytes a mais para cada elemento da sua lista
  • O inserto é linear no tempo: O (n)
  • Remover é uma operação constante: O (1)
  • Acesse o elemento x é linear no tempo: O (n)

Vetores:

  • Precisa de menos memória (sem ponteiros para outros elementos, é um algoritmo matemático simples)
  • Remover é linear no tempo: O (n)
  • O acesso ao elemento x é constante: O (1) (Isso ocorre porque os elementos são contínuos na memória, por isso é uma operação matemática simples vectorPtr + (x * bytesOfTheType))
  • A inserção pode ocorrer em tempo linear, mas, mais comumente, é uma operação constante: O (1) (isso ocorre porque o vetor em uma matriz sempre reserva 2 vezes sua capacidade quando a matriz está cheia, para que a cópia da matriz não seja frequente)

Portanto, a lista é melhor quando o programa precisa adicionar e remover elementos com frequência, mas nunca acesse (ou raramente acesse) um elemento específico sem a necessidade dos outros antes. O vetor deve ser usado para melhorar o tempo de acesso, mas falta eficiência quando você precisa remover ou adicionar elementos.

Verifique esta postagem no stackoverflow, ela apresenta um gráfico muito bom, com perguntas básicas sobre suas necessidades, que o levam a um contêiner específico, dependendo de suas respostas:

/programming/366432/extending-stdlist

Frédérick Imbeault
fonte
3
Esse gráfico realmente precisa começar com um nó "Você está armazenando ponteiros e menos de mil? Não -> vetor".
Sim, talvez você esteja certo, eu não considerei o tipo armazenado, ele também deve ser analisado.
Frédérick Imbeault 27/10/10
2

Geralmente, as listas são usadas para estruturas como filas, nas quais existem muitas operações de acréscimo e remoção. Exemplo: uma lista em constante mudança de entidades que devem ser atualizadas. A lista em si contém apenas entidades na tela e, portanto, muda frequentemente.

Os vetores (ou matrizes) são mais adequados para uma coleção que não muda muito e onde você precisa de acesso rápido a itens individuais da coleção. Exemplo: um mapa de blocos em que você precisa procurar blocos em um determinado índice.

A opinião do Tetrads pode ser verdadeira, mas depende da linguagem de programação usada. Vejo que você marcou sua pergunta c++, mas tentei dar uma resposta que não é específica ao idioma.

bummzack
fonte
Bem, eu também poderia ter colocado C nele, mas não existem esses contêineres em C, mas isso é algo para se pensar: existe alguma biblioteca semelhante a STL para C?
Jokoon 27/10/10
Eu usei glib ( library.gnome.org/devel/glib ) no passado, o que implementa várias estruturas de dados padrão para C. Eu odiava isso porque geralmente é muito detalhado e quer desesperadamente ser C ++, mas é maduro e estábulo.
0

Nos jogos de console, nunca usamos std :: list porque:

  1. ele faz alocações de memória sempre que você adiciona um novo item. as alocações de memória são lentas.
  2. está cheio de ponteiros. ponteiros são ruins. um ponteiro é um erro de cache. uma falta de cache é ruim.

Até o std :: vector está perdendo o interesse nos consoles porque:

  1. você geralmente se preocupa apenas com, por exemplo, as posições de todos os objetos. por exemplo, você deseja colidir os objetos entre si, caso em que não se importa com a cor deles. para que todas as posições sejam contíguas na memória e as cores estejam em outro lugar distante, para evitar poluir o cache. mas std :: vector exige que você armazene tudo para cada objeto em um pedaço contíguo de memória (por exemplo, posição e cor). Portanto, se você trabalha lendo apenas as posições, também precisa ler todas as cores no cache, até se você não os usar. isso é um desperdício.
bmcnett
fonte
5
@bmcnett "[] .. mas std :: vector exige que você armazene tudo para cada objeto em um pedaço contíguo de memória" - isso não é uma questão do contêiner, é uma questão de seu layout de dados, você pode ter todas as posições em um pedaço contínuo de memória com um std :: vector:struct point{float x, y, z, w}; std::vector<point> positions;
Maik Semder
minha escola vão adorar este :)
jokoon
2
-1 porque esta resposta não adiciona nada à lista vs. discussão vetorial já aqui, e sua reivindicação sobre poluir o vetor no cache está errada, como diz Maik.
você não entendeu minha reivindicação. Eu nunca disse que entre todos os contêineres o std :: vector é particularmente culpado por poluir o cache. todos os contêineres, e de fato até arrays, são igualmente culpados disso. O std :: vector está desvalorizando porque os próprios objetos C ++ estão desvalorizando. O C ++ exige que os dados de cada objeto sejam contíguos na memória. você pode contornar isso evitando objetos C ++, por exemplo, usando std :: vector <position> como mencionado acima.
bmcnett
3
Exceto pointé um objeto C ++ (como é std::vector, como é algo tão simples quanto float). Eu sei a distinção que você está tentando fazer, mas está fazendo um mau trabalho de explicá-la.