Perché preferirei usare il vettore per eliminare


87

Da

  1. sono entrambi contenitori di memoria contigui;
  2. dal punto di vista delle funzionalità, deque ha quasi tutto ciò che vector ha ma di più, poiché è più efficiente da inserire in primo piano.

Perché qualcuno std::vectordovrebbe preferirlo std::deque?


2
L'implementazione di Visual C ++ di std::dequeha una dimensione massima del blocco molto piccola (~ 16 byte, se ricordo bene; forse 32), e come tale non funziona molto bene per applicazioni realistiche. A deque<T>dove sizeof(T) > 8(o 16? È un numero piccolo) ha circa le stesse caratteristiche di prestazione di a vector<T*>, dove ogni elemento è allocato dinamicamente. Altre implementazioni hanno dimensioni massime di blocco diverse, quindi è difficile scrivere codice che abbia relativamente le stesse caratteristiche di prestazioni su piattaforme diverse deque.
James McNellis

13
Deque non è un contenitore di memoria continua.
Mohamed El-Nakib

@ravil No, questo è il duplicato, indicando questa domanda.

1
difficile credere che una domanda con un errore di fatto palese e non risolto fosse seduta su un bilancio di 34 voti
underscore_d

2
@underscore_d ecco perché è una domanda. Storia diversa se fosse una risposta;)
Assimilater

Risposte:


115

Gli elementi in a nondeque sono contigui nella memoria; gli elementi sono garantiti. Quindi, se hai bisogno di interagire con una semplice libreria C che necessita di array contigui, o se ti interessa (molto) la località spaziale, allora potresti preferire . Inoltre, poiché c'è qualche contabilità extra, altre operazioni sono probabilmente (leggermente) più costose delle loro operazioni equivalenti . D'altra parte, l'utilizzo di molte / grandi istanze di può portare a una frammentazione dell'heap non necessaria (rallentando le chiamate a ).vectorvectorvectorvectornew

Inoltre, come sottolineato altrove su StackOverflow , c'è una discussione più interessante qui: http://www.gotw.ca/gotw/054.htm .


3
Sembra che il collegamento a "altrove" sia ora morto (a causa della moderazione?).
esilk

37

Per conoscere la differenza si dovrebbe sapere come dequeviene generalmente implementato. La memoria è allocata in blocchi di dimensioni uguali e sono concatenati insieme (come un array o eventualmente un vettore).

Quindi, per trovare l'ennesimo elemento, trova il blocco appropriato e accedi all'elemento al suo interno. Questo è un tempo costante, perché è sempre esattamente 2 ricerche, ma è ancora più del vettore.

vectorfunziona bene anche con le API che desiderano un buffer contiguo perché sono API C o sono più versatili in quanto possono prendere un puntatore e una lunghezza. (Quindi puoi avere un vettore sotto o un array normale e chiamare l'API dal tuo blocco di memoria).

Dove dequeha i suoi maggiori vantaggi sono:

  1. Quando si cresce o si restringe la collezione da entrambe le estremità
  2. Quando hai a che fare con raccolte di dimensioni molto grandi.
  3. Quando si ha a che fare con bool e si desidera davvero bool piuttosto che un set di bit.

Il secondo di questi è meno conosciuto, ma per formati di raccolta molto grandi:

  1. Il costo della riallocazione è elevato
  2. Il sovraccarico di dover trovare un blocco di memoria contiguo è restrittivo, quindi puoi esaurire la memoria più velocemente.

Quando in passato avevo a che fare con raccolte di grandi dimensioni e passavo da un modello contiguo a un modello a blocchi, siamo stati in grado di archiviare una raccolta circa 5 volte più grande prima di esaurire la memoria in un sistema a 32 bit. Ciò è in parte dovuto al fatto che, durante la riallocazione, era effettivamente necessario memorizzare il vecchio blocco oltre a quello nuovo prima di copiare gli elementi.

Detto questo, potresti avere problemi con i std::dequesistemi che utilizzano l'allocazione di memoria "ottimistica". Sebbene i suoi tentativi di richiedere una grande dimensione del buffer per una riallocazione di a vectorverranno probabilmente rifiutati ad un certo punto con a bad_alloc, è probabile che la natura ottimistica dell'allocatore garantisca sempre la richiesta per il buffer più piccolo richiesto da a dequee questo il sistema operativo per terminare un processo per cercare di acquisire memoria. Qualunque cosa scelga potrebbe non essere troppo piacevole.

La soluzione alternativa in questo caso consiste nell'impostare flag a livello di sistema per sovrascrivere l'allocazione ottimistica (non sempre fattibile) o gestire la memoria un po 'più manualmente, ad esempio utilizzando il proprio allocatore che controlla l'utilizzo della memoria o simili. Ovviamente non è l'ideale. (Che potrebbe rispondere alla tua domanda come preferire il vettore ...)


30

Ho implementato sia vector che deque più volte. deque è enormemente più complicato dal punto di vista dell'implementazione. Questa complicazione si traduce in più codice e codice più complesso. Quindi in genere vedrai una dimensione del codice colpita quando scegli deque sul vettore. Potresti anche riscontrare un leggero aumento della velocità se il tuo codice utilizza solo le cose in cui il vettore eccelle (cioè push_back).

Se hai bisogno di una coda doppia, deque è il chiaro vincitore. Ma se stai facendo la maggior parte dei tuoi inserti e cancellazioni sul retro, il vettore sarà il chiaro vincitore. Quando non sei sicuro, dichiara il tuo contenitore con un typedef (quindi è facile passare avanti e indietro) e misura.


3
Domanda: il comitato ha preso in considerazione l'aggiunta di un ibrido dei due (ad esempio, un "mazzo") a C ++? (cioè un doppio attacco vector.) Ho scritto un'implementazione collegata a sotto nella mia risposta . Può essere veloce come un vectorma molto più ampiamente applicabile (ad esempio, quando si effettua una coda veloce).
user541686

5

std::dequenon ha una memoria continua garantita, ed è spesso un po 'più lento per l'accesso indicizzato. Un deque è tipicamente implementato come un "elenco di vettori".


14
Non penso che un "elenco di vettori" sia corretto: la mia comprensione era che la maggior parte delle implementazioni erano un "vettore di puntatori ad array", sebbene dipenda dalla tua definizione di "elenco" (ho letto "elenco" come "elenco collegato , "che non soddisferebbe i requisiti di complessità.)
James McNellis

2

Secondo http://www.cplusplus.com/reference/stl/deque/ , "a differenza dei vettori, non è garantito che i deques abbiano tutti i suoi elementi in posizioni di archiviazione contigue, eliminando così la possibilità di un accesso sicuro attraverso l'aritmetica dei puntatori".

Le Deques sono un po 'più complicate, in parte perché non hanno necessariamente un layout di memoria contiguo. Se hai bisogno di quella funzione, non dovresti usare un deque.

(In precedenza, la mia risposta sollevava una mancanza di standardizzazione (dalla stessa fonte di cui sopra, "i deques possono essere implementati da librerie specifiche in modi diversi"), ma questo in realtà si applica a quasi tutti i tipi di dati di libreria standard.)


5
std::dequenon è meno standardizzato di std::vector. Non credo che i requisiti di complessità per std::dequepossano essere soddisfatti con l'archiviazione contigua.
Jerry Coffin

1
Forse il mio fraseggio era scadente: sebbene sia vero che la standardizzazione non è completa, a quanto ho capito, i vettori sono standardizzati per essere una sequenza conitugua, e i deques non lo sono. Questo sembra essere l'unico fattore decisivo.
pattivacek

1
@ JerryCoffin: quali requisiti di complessità dequenon possono essere soddisfatti con l'archiviazione contigua?
user541686

1
@Mehrdad: Ad essere onesti, non ricordo cosa avevo in mente. Non ho esaminato quella parte dello standard di recente abbastanza da sentirmi a mio agio nell'affermare categoricamente che il mio precedente commento era sbagliato, ma guardandolo in questo momento, non riesco nemmeno a pensare a come sarebbe giusto.
Jerry Coffin

3
@ JerryCoffin: I requisiti di complessità sono in realtà banali: potresti allocare un array di grandi dimensioni e iniziare a spingere la sequenza dal centro verso l'esterno (immagino che questo sia ciò che fa l'implementazione di Mehrdad), quindi riallocare quando arrivi alla fine. Il problema con questo approccio è che non soddisfa uno dei requisiti di deque, vale a dire che l'inserimento alle estremità non invalida il riferimento agli elementi esistenti. Questo requisito implica una memoria discontinua.
Yakov Galka

0

Un deque è un contenitore di sequenze che consente l'accesso casuale ai suoi elementi ma non è garantito che abbia una memoria contigua.


0

Penso che sia una buona idea fare il test delle prestazioni di ogni caso. E prendi una decisione basandoti su questi test.

Preferisco std::dequeche std::vectornella maggior parte dei casi.


1
La domanda, se ce n'è una può essere distillata tra gli errori fattuali e le parole mancanti, era perché qualcuno avrebbe preferito vector. Possiamo dedurre che perché no è un corollario. Dire che preferisci deque, per ragioni sconosciute, da test non specificati, non è una risposta.
underscore_d


0

Da un lato, il vettore è abbastanza spesso semplicemente più veloce di deque. Se in realtà non hai bisogno di tutte le funzionalità di deque, usa un vettore.

D'altra parte, a volte si fa caratteristiche bisogno che vettore non ti dà, nel qual caso è necessario utilizzare un deque. Ad esempio, sfido chiunque a tentare di riscrivere questo codice , senza utilizzare una deque e senza alterare enormemente l'algoritmo.


In realtà, sulla stessa serie di push_backe pop_backoperazioni, deque<int>è sempre almeno il 20% più veloce rispetto vector<int>ai miei test (gcc con O3). Immagino sia per questo che dequeè la scelta standard per cose come std::stack...
igel

-1

Notare che la memoria del vettore viene riassegnata man mano che l'array cresce. Se si dispone di puntatori a elementi vettoriali, non saranno più validi.

Inoltre, se cancelli un elemento, gli iteratori diventano non validi (ma non "for (auto ...)").

Modifica: cambiato "deque" in "vector"


-1: l'inserimento e la cancellazione all'inizio o alla fine NON invalida riferimenti e puntatori ad altri elementi. (Anche se inserire e cancellare nel mezzo fa)
Sjoerd

@Sjoerd l'ho cambiato in 'vector'.
Pierre
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.