Quando utilizzare il vettore / elenco?


17

Riesco a capire quando usare gli elenchi, ma non capisco quando è meglio usare i vettori che usare gli elenchi nei videogiochi: quando è meglio avere un accesso casuale veloce?

(E capisco perché è più veloce inserire / eliminare nelle liste perché rimuove / aggiunge solo puntatori, ma deve ancora trovare l'elemento corrispondente ...)


Nuovo tag vettoriale aggiunto a questo: se l'elenco è un tag valido, lo è anche il vettore.
Kylotan,

Presumibilmente il vettore è stato rimosso perché è usato per significare vettore matematico, non std :: vector.

1
che ne dici di nix entrambi e mettere container.
deft_code

@Kylotan: È come ha detto Joe. Questa domanda riguardava sicuramente i vettori, ma non apparteneva al tag vector.
doppelgreener,

1
Quindi rimuoviamo qualche tag ambiguo? Mi sembra una decisione sbagliata, meglio che la tua ricerca fornisca troppe informazioni piuttosto che non sufficienti. Saltare i risultati indesiderati è più facile del brainstorming per trovare sinonimi.
Kylotan,

Risposte:


29

La mia regola empirica, e sono sicuro che ci sarà un dibattito su questo, è di non usare mai le liste (a meno che non sia necessario rimuovere molto, molto frequentemente le cose dal mezzo di grandi liste).

La velocità che otterrai avendo tutti i tuoi elementi nel tuo contenitore in memoria contigua (e quindi più compatibile con la cache) vale la compensazione dei costi aggiuntivi di aggiunta / rimozione / ridimensionamento del vettore.

Modifica: solo per chiarire un po 'di più, ovviamente dovrebbe essere ovvio che qualsiasi tipo di domanda "che è più veloce" dovrebbe essere testata su qualsiasi piattaforma con qualunque set di dati sia pertinente alle tue esigenze particolari. Se ho solo bisogno di una raccolta di elementi, uso solo vector (o deque, che è quasi la stessa cosa) a meno che non ci sia una buona ragione per non farlo.


1
Penso che dipenda fortemente dalle tue esigenze, se non hai mai bisogno di accedere a un elemento specifico e devi solo leggerli tutti e aggiungere e rimuovere elementi di frequente, l'elenco è una soluzione migliore.
Frédérick Imbeault,

11
@ Frédérick: questa è la saggezza C ++ standard, ma è anche quasi sempre sbagliata. I vettori, specialmente quando si tratta di vettori di puntatori (che quasi sempre sono per i giochi), sono estremamente veloci per rimuovere roba da metà - è un tempo lineare, ma è un overhead molto piccolo per articolo. È anche molto più veloce iterare in sequenza su un vettore.

1
Non sono sicuro del tipo di struttura che stai ottenendo esattamente - questo tipo di ottimizzazione richiede esempi concreti per dire qualcosa in modo definitivo. Ad esempio, la mia prima reazione al tuo caso d'uso sarebbe una serie non ordinata, che consente inserimenti, eliminazioni e ricerche altrettanto rapidi; nella mia esperienza i set sono fantastici per gli oggetti negli editor. Ma poiché i redattori hanno requisiti prestazionali in tempo reale molto più lassisti, ad es. Va bene se la risposta alla pressione di un pulsante "Elimina" richiede 1/20 di secondo o un 1/2% di secondo il 10% delle volte - questo livello di ottimizzazione si applica raramente anche a loro.

1
@ Frédérick Imbeault Modified non è un grosso problema, è Add \ Remove che causa problemi. Da Aggiungi \ rimosso punto alla fine del vettore viene copiato per mantenere contiguo il vettore. Se l'ordine degli elementi non ha importanza, puoi scambiare l'elemento eliminato con l'ultimo elemento, quindi aprirlo per eliminare e aggiungere alla fine per aggiungere.
stonemetal

4
Bene, prendere l'indirizzo di un elemento in un vettore e memorizzarlo non è sicuro. Ma in generale non lo fai quasi mai, preferendo invece avere un vettore di puntatori di elementi e copiando il puntatore in giro (o qualcosa di simile).
Tetrad,

8

Utilizzare un elenco quando l'invalidazione dell'iteratore causata dalla modifica della parte centrale della struttura dei dati causerà un problema, oppure è necessario mantenere gli elementi ordinati in modo che il trucco di scambio e pop per le eliminazioni rapide della raccolta centrale non funzionerà e si dispone di un grande numero di eliminazioni a metà raccolta.

Potresti anche considerare di usare un Deque. Ha caratteristiche prestazionali simili a un vettore ma non ha bisogno del vettore di memoria contigua ed è un po 'più flessibile.


+1 per essere l'unica persona a menzionare i deques: ottieni i vantaggi della memoria contigua e della velocità di ricerca dei vettori, con inserimento / rimozione rapidi su entrambe le estremità.

5

La tua scelta dovrebbe riflettere le tue esigenze. Tutti gli elementi dei vettori sono continui in memoria e gli elenchi hanno puntatori agli elementi successivi / precedenti in modo che ciascuno di essi abbia il proprio vantaggio / svantaggio:

Elenchi:

  • Ogni elemento richiede 2 numeri interi per puntare elementi precedenti e successivi, quindi più comunemente, sono 8 byte in più per ogni elemento nell'elenco
  • L'inserimento è lineare nel tempo: O (n)
  • Rimuovere è un'operazione costante: O (1)
  • L'accesso all'elemento x è lineare nel tempo: O (n)

Vettori:

  • Richiede meno memoria (nessun puntatore ad altri elementi, è un semplice algoritmo matematico)
  • Rimuovi è lineare nel tempo: O (n)
  • L'accesso all'elemento x è costante: O (1) (Questo perché gli elementi sono continui in memoria, quindi è una semplice operazione matematica vectorPtr + (x * bytesOfTheType))
  • L'inserimento può essere in tempo lineare, ma più comunemente è un'operazione costante: O (1) (Questo perché il vettore in un array ma riserva sempre 2 volte la sua capacità quando l'array è pieno, quindi la copia dell'array non è frequente)

Quindi l'elenco è meglio quando il programma deve aggiungere e rimuovere elementi frequentemente, ma non accedere mai (o raramente accedere) a un elemento particolare senza la necessità degli altri prima. Il vettore dovrebbe essere usato per un miglior tempo di accesso, ma manca di efficacia quando è necessario rimuovere o aggiungere elementi.

Controlla questo post su stackoverflow, presenta un grafico davvero carino con domande di base sulle tue esigenze che ti porta in un contenitore specifico in base alle tue risposte:

/programming/366432/extending-stdlist


3
Quel grafico deve davvero iniziare con un nodo "Stai memorizzando i puntatori e meno di un migliaio? No -> vettore".

Sì forse hai ragione, non ho considerato il tipo memorizzato che dovrebbe essere analizzato.
Frédérick Imbeault,

2

Solitamente gli elenchi vengono utilizzati per strutture come le code in cui sono presenti molte operazioni di aggiunta e rimozione. Esempio: un elenco in continua evoluzione di entità che dovrebbero essere aggiornate. L'elenco stesso contiene solo entità sullo schermo e pertanto cambia frequentemente.

I vettori (o le matrici) sono più adatti per una raccolta che non cambia molto e in cui è necessario un accesso rapido ai singoli elementi all'interno della raccolta. Esempio: una mappa di tessere in cui devi cercare tessere in un determinato indice.

L'opinione di Tetrad potrebbe essere vera, ma dipende dal linguaggio di programmazione utilizzato. Vedo che hai taggato la tua domanda c++, ma ho provato a dare una risposta che non è specifica della lingua.


Beh, potrei anche aver messo C in esso, ma non ci sono tali contenitori in C, ma è qualcosa a cui pensare: esiste un tibrario simile a STL per C?
jokoon

Ho usato glib ( library.gnome.org/devel/glib ) in passato, che implementa una serie di strutture di dati standard per C. Lo odiavo perché di solito è troppo verboso e vuole disperatamente essere C ++, ma è maturo e stabile.

0

Nei giochi per console non usiamo mai e poi mai std :: list perché:

  1. esegue allocazioni di memoria ogni volta che aggiungi un nuovo elemento. le allocazioni di memoria sono lente.
  2. è pieno di puntatori. i puntatori sono cattivi. un puntatore è un errore cache. un errore nella cache è negativo.

anche std :: vector sta perdendo favore sulle console perché:

  1. ti preoccupi spesso solo, ad esempio, delle posizioni di tutti gli oggetti. come ad esempio vuoi scontrare gli oggetti l'uno con l'altro, nel qual caso non ti interessa quale sia il loro colore. quindi vuoi che tutte le posizioni siano contigue nella memoria e vuoi che i colori siano da qualche altra parte lontani, per evitare di inquinare la cache. ma std :: vector richiede di archiviare tutto per ogni oggetto in un pezzo contiguo di memoria (ad es. posizione quindi colore.) quindi se fai un lavoro che legge solo le posizioni, devi anche leggere tutti i colori nella cache, anche se non li usi. questo è uno spreco.

5
@bmcnett "[] .. ma std :: vector richiede di archiviare tutto per ogni oggetto in un pezzo contiguo di memoria" - questa non è una questione del contenitore, è una questione di layout dei dati, puoi avere tutte le posizioni in un pezzo continuo di memoria con uno std :: vector:struct point{float x, y, z, w}; std::vector<point> positions;
Maik Semder

la mia scuola
adorerà

2
-1 perché questa risposta non aggiunge nulla all'elenco rispetto alla discussione vettoriale qui e la sua affermazione sull'inquinamento vettoriale della cache è errata, come dice Maik.

hai frainteso la mia richiesta. non ho mai detto che tra tutti i contenitori std :: vector sia particolarmente colpevole di inquinare la cache. tutti i contenitori, e in effetti persino gli array, ne sono ugualmente colpevoli. std :: vector sta cadendo in disgrazia perché gli oggetti C ++ stessi stanno sfuggendo a favore. Il C ++ richiede che i dati di ciascun oggetto siano contigui nella memoria. puoi aggirare questo evitando oggetti C ++, ad esempio usando std :: vector <position> come menzionato sopra.
bmcnett,

3
Tranne che pointè un oggetto C ++ (così com'è std::vector, com'è qualcosa di così semplice float). Conosco la distinzione che stai cercando di disegnare, ma stai facendo un brutto lavoro nel spiegarlo.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.