Concatenare due std :: vettori


687

Come posso concatenare due std::vectors?


5
Le risposte fornite non si concatenano. Aggiungono una copia. Potrebbe esserci un uso (per motivi di efficienza) per creare un metodo concatenato std :: vector, tuttavia richiederebbe una condivisione sofisticata della gestione dei nodi ed è probabilmente per questo che non è stato fatto.
FauChristian,

8
@FauChristian: No, potrebbe non esserci un uso dal punto di vista dell'efficienza. La memoria vettoriale deve essere continua, quindi ciò che viene suggerito è impossibile. Se volessi "una condivisione sofisticata della gestione dei nodi", e se cambiassi la classe vettoriale in questo modo, finiresti con un deque. Anche allora è molto difficile riutilizzare la memoria nel modo suggerito, sebbene inizierebbe ad essere un po 'più fattibile. Non penso che sia attualmente implementato. La cosa principale è che in una tale condivisione di nodi di gestione (un deque) il nodo finale potrebbe essere parzialmente vuoto.
Cookie

4
@lecaruyer Ti rendi conto di aver appena segnato una domanda che era stata posta due anni prima come duplicato
eshirima,

9
Sono l'unico a chiedersi perché questo non è implementato come a + bo a.concat(b)nella libreria standard? Forse l'implementazione predefinita sarebbe subottimale, ma ogni concatenazione di array non deve essere micro-ottimizzata
oseiskar

10
anni di evoluzione, il sovraccarico dell'operatore più avanzato di qualsiasi linguaggio tradizionale, un sistema di modelli che raddoppia la complessità della lingua, eppure la risposta non è v = v1 + v2;
Spike0xff,

Risposte:


728
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );

53
Aggiungerei solo il codice per ottenere prima il numero di elementi contenuti in ciascun vettore e impostare vector1 come quello che detiene il massimo. Se dovessi fare diversamente, stai facendo molte copie non necessarie.
Joe Pineda,

34
Ho una domanda. Funzionerà se vector1 e vector2 sono gli stessi vettori?
Alexander Rafferty,

6
Se si concatenano diversi vettori in uno, è utile chiamare reserveprima il vettore di destinazione?
Faheem Mitha,

33
@AlexanderRafferty: solo se vector1.capacity() >= 2 * vector1.size(). Che è atipico a meno che tu non abbia chiamato std::vector::reserve(). Altrimenti il ​​vettore verrà riallocato, invalidando gli iteratori passati come parametri 2 e 3.
Drew Dormann,

28
Peccato che non ci sia un'espressione più concisa nella libreria standard. .concato +=qualcosa del genere
nmr

193

Se si utilizza C ++ 11 e si desidera spostare gli elementi anziché semplicemente copiarli, è possibile utilizzare std::move_iteratorinsieme a insert (o copia):

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<int> dest{1,2,3,4,5};
  std::vector<int> src{6,7,8,9,10};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  // Print out concatenated vector.
  std::copy(
      dest.begin(),
      dest.end(),
      std::ostream_iterator<int>(std::cout, "\n")
    );

  return 0;
}

Questo non sarà più efficiente per l'esempio con ints, poiché spostarli non è più efficiente della loro copia, ma per una struttura di dati con spostamenti ottimizzati, può evitare di copiare lo stato non necessario:

#include <vector>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
  std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
  std::vector<std::vector<int>> src{{6,7,8,9,10}};

  // Move elements from src to dest.
  // src is left in undefined but safe-to-destruct state.
  dest.insert(
      dest.end(),
      std::make_move_iterator(src.begin()),
      std::make_move_iterator(src.end())
    );

  return 0;
}

Dopo lo spostamento, l'elemento src viene lasciato in uno stato indefinito ma sicuro da distruggere, e i suoi elementi precedenti sono stati trasferiti direttamente al nuovo elemento di dest alla fine.


6
Il metodo std :: make_move_iterator () mi ha aiutato quando ho cercato di concatenare std :: vettori di std :: unique_ptr.
Knitschi,

Qual è la differenza tra questo e std::move(src.begin(), src.end(), back_inserter(dest))?
kshenoy,


77

Oppure potresti usare:

std::copy(source.begin(), source.end(), std::back_inserter(destination));

Questo modello è utile se i due vettori non contengono esattamente lo stesso tipo di cose, perché è possibile utilizzare qualcosa invece di std :: back_inserter per convertire da un tipo all'altro.


7
il metodo di copia non è un buon modo. Chiamerà push_back più volte, il che significa che se devono essere inseriti molti elementi questo potrebbe significare riallocazioni multiple. è meglio usare insert poiché l'implementazione vettoriale potrebbe fare qualche ottimizzazione per evitare riallocazioni. potrebbe riservare memoria prima di iniziare a copiare
Yogesh Arora,

7
@Yogesh: scontato, ma non c'è niente che ti impedisca di chiamare per reserveprimo. Il motivo std::copyè talvolta utile se si desidera utilizzare qualcosa di diverso back_inserter.
Roger Lipscombe,

Quando si dice "allocazioni multiple", ciò è vero, ma il numero di allocazioni è nel peggiore registro (numero di voci aggiunte) - il che significa che il costo dell'aggiunta di una voce è costante nel numero di voci aggiunte. (Fondamentalmente, non ti preoccupare a meno che la profilazione non mostri che hai bisogno di una riserva).
Martin Bonner supporta Monica il

Potresti voler usare std :: transform per fare questo invece.
Martin Broadhurst,

1
copia fa schifo molto, anche con riserva. vector :: insert eviterà tutti i controlli: quick-bench.com/bLJO4OfkAzMcWia7Pa80ynwmAIA
Denis Yaroshevskiy

63

Con C ++ 11, preferirei aggiungere il vettore b a a:

std::move(b.begin(), b.end(), std::back_inserter(a));

quando ae bnon sono sovrapposti e bnon verranno più utilizzati.


Questo è std::movedi <algorithm>, non il solito std::move da <utility>.


10
Comportamento indefinito se a in realtà è b (il che è OK se si sa che non può mai accadere - ma vale la pena essere consapevoli del codice generico).
Martin Bonner supporta Monica il

1
@MartinBonner Grazie per averlo menzionato. Probabilmente dovrei tornare al vecchio insertmodo che è più sicuro.
Deqing,

15
Ah, l'ALTRO std :: move. Abbastanza confuso la prima volta che lo vedi.
xaxxon,

1
È diverso da insert()con move_iterators? Se é cosi, come?
GPhilo,

1
Ho aggiunto una nota su ciò di std::movecui stiamo parlando qui, poiché la maggior parte delle persone non conosce questo sovraccarico. Spero sia un miglioramento.
YSC

38
std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());

24

Preferisco uno che è già menzionato:

a.insert(a.end(), b.begin(), b.end());

Ma se usi C ++ 11, c'è un altro modo generico:

a.insert(std::end(a), std::begin(b), std::end(b));

Inoltre, non fa parte di una domanda, ma è consigliabile utilizzarlo reserveprima di aggiungere una prestazione migliore. E se stai concatenando il vettore con se stesso, senza riservarlo, fallisci, quindi dovresti sempre reserve.


Quindi sostanzialmente quello che ti serve:

template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
    a.reserve(a.size() + b.size());
    a.insert(a.end(), b.begin(), b.end());
}

2
std::viene dedotto tramite la ricerca dipendente dall'argomento . end(a)sarà abbastanza.
Asu,

4
@Asu ADL aggiungerà solo std::se il tipo di aderiva dal std, che sconfigge l'aspetto generico.
Potatoswatter il

buon punto. in questo caso è un vettore quindi funzionerebbe comunque, ma sì, questa è una soluzione migliore.
Asu

sono stati aggiunti std :: begin () / end () per le raccolte (come gli array) che non li hanno come funzioni membro. Ma anche le matrici non hanno una funzione membro insert () e chiama la domanda "Esiste una raccolta con un insert () ma senza begin () (che funziona con lo std :: begin ())?"
James Curran,


12

Dovresti usare vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());

3
Non è lo stesso con la risposta data da Tom Ritter e Robert Gamble nel 2008?
IgNite

9

Un aumento generale delle prestazioni per il concatenato è il controllo delle dimensioni dei vettori. E unisci / inserisci quello più piccolo con quello più grande.

//vector<int> v1,v2;
if(v1.size()>v2.size()) {
    v1.insert(v1.end(),v2.begin(),v2.end());
} else {
    v2.insert(v2.end(),v1.begin(),v1.end());
}

Così semplice, ma non ci ho mai pensato in quel modo!
Zimano,

2
Il codice di esempio non è corretto. v1.insert(v2.end()...sta usando un iteratore in v2per specificare la posizione in v1.
David Stone,

Puoi anche usare uno scambio veloce. @DavidStone L'ho modificato in modo che l'ordine di concat possa essere modificato. È possibile aggiungere all'inizio di un vettore?
qwr

Puoi inserirlo all'inizio, ma sarà più lento. Per "concatenare" veramente, tuttavia, l'ordine in genere è importante, quindi è quello che devi fare.
David Stone,

7

Se si desidera essere in grado di concatenare i vettori in modo conciso, è possibile sovraccaricare l' +=operatore.

template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
    vector1.insert(vector1.end(), vector2.begin(), vector2.end());
    return vector1;
}

Quindi puoi chiamarlo così:

vector1 += vector2;

6

Se sei interessato alla garanzia di eccezioni forti (quando il costruttore di copie può generare un'eccezione):

template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
    const auto orig_v1_size = v1.size();
    v1.reserve(orig_v1_size + v2.size());
    try
    {
        v1.insert(v1.end(), v2.begin(), v2.end());
    }
    catch(...)
    {
        v1.erase(v1.begin() + orig_v1_size, v1.end());
        throw;
    }
}

Simile append_movecon una forte garanzia non può essere implementato in generale se il costruttore di mosse di elementi vettoriali può lanciare (il che è improbabile ma comunque).


Non è possibile v1.erase(...anche lanciare?
Scheletro di classe,

insertgestisce già questo. Inoltre, questa chiamata a eraseequivale a resize.
Potatoswatter il

Mi piace questo - grazie!
natersoz,

5

Aggiungi questo al tuo file di intestazione:

template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
    vector<T> ret = vector<T>();
    copy(a.begin(), a.end(), back_inserter(ret));
    copy(b.begin(), b.end(), back_inserter(ret));
    return ret;
}

e usalo in questo modo:

vector<int> a = vector<int>();
vector<int> b = vector<int>();

a.push_back(1);
a.push_back(2);
b.push_back(62);

vector<int> r = concat(a, b);

r conterrà [1,2,62]


Non so perché sia ​​stato votato in negativo. Potrebbe non essere il modo più efficiente per farlo, ma non è sbagliato ed è efficace.
leeor_net,

4

Ecco una soluzione per scopi generici che utilizza la semantica di spostamento C ++ 11:

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
    if (lhs.empty()) return rhs;
    if (rhs.empty()) return lhs;
    std::vector<T> result {};
    result.reserve(lhs.size() + rhs.size());
    result.insert(result.cend(), lhs.cbegin(), lhs.cend());
    result.insert(result.cend(), rhs.cbegin(), rhs.cend());
    return result;
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
    lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
    return std::move(lhs);
}

template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
    rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
    return std::move(rhs);
}

template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
    if (lhs.empty()) return std::move(rhs);
    lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
    return std::move(lhs);
}

Nota come questo differisce da appending a vector.


4

Puoi preparare il tuo modello per + operatore:

template <typename T> 
inline T operator+(const T & a, const T & b)
{
    T res = a;
    res.insert(res.end(), b.begin(), b.end());
    return res;
}

La prossima cosa: basta usare +:

vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
    cout << x << " ";
cout << endl;

Questo esempio fornisce output:

1 2 3 4 5 6 7 8

3
L'uso T operator+(const T & a, const T & b)è pericoloso, è meglio usare vector<T> operator+(const vector<T> & a, const vector<T> & b).
Matthieu H,

4

Esiste un algoritmo std::mergedi C ++ 17 , che è molto facile da usare,

Di seguito è riportato l'esempio:

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    //DATA
    std::vector<int> v1{2,4,6,8};
    std::vector<int> v2{12,14,16,18};

    //MERGE
    std::vector<int> dst;
    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));

    //PRINT
    for(auto item:dst)
        std::cout<<item<<" ";

    return 0;
}

3
Non credo sia più facile da usare rispetto a std::vector::insert, ma lo fa in qualcosa di diverso: fondere due intervalli in un nuovo intervallo anziché inserire un vettore alla fine di un altro. Vale la pena menzionare nella risposta?
jb

4

Se il tuo obiettivo è semplicemente quello di iterare l'intervallo di valori per scopi di sola lettura, un'alternativa è avvolgere entrambi i vettori attorno a un proxy (O (1)) invece di copiarli (O (n)), in modo che vengano immediatamente visualizzati come singolo, contiguo.

std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };

VecProxy<int> AB(A, B);  // ----> O(1)!

for (size_t i = 0; i < AB.size(); i++)
    std::cout << AB[i] << " ";  // ----> 1 2 3 4 5 10 20 30

Fare riferimento a https://stackoverflow.com/a/55838758/2379625 per maggiori dettagli, inclusa l'implementazione "VecProxy", nonché i pro e contro.


3
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));

6
Sebbene questo frammento di codice possa risolvere il problema, non spiega perché o come risponde alla domanda. Includi una spiegazione per il tuo codice , in quanto ciò aiuta davvero a migliorare la qualità del tuo post. Flaggers / recensori: per le risposte di solo codice come questo, downvote, non eliminare! (Nota: questa risposta potrebbe in realtà essere abbastanza semplice da rendere una spiegazione, e quindi downvotes, non necessaria. Potresti comunque voler aggiungere una spiegazione per prevenire più bandiere NAA / VLQ.)
Scott Weldon,

2

Ho implementato questa funzione che concatena qualsiasi numero di contenitori, spostandomi da riferimenti a valori e copiando altrimenti

namespace internal {

// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
    // Currently, require each homogenous inputs. If there is demand, we could probably implement a
    // version that outputs a vector whose value_type is the common_type of all the containers
    // passed to it, and call it ConvertingConcatenate.
    static_assert(
            std::is_same_v<
                    typename std::decay_t<Target>::value_type,
                    typename std::decay_t<Head>::value_type>,
            "Concatenate requires each container passed to it to have the same value_type");
    if constexpr (std::is_lvalue_reference_v<Head>) {
        std::copy(head.begin(), head.end(), std::back_inserter(*target));
    } else {
        std::move(head.begin(), head.end(), std::back_inserter(*target));
    }
    if constexpr (sizeof...(Tail) > 0) {
        AppendNoReserve(target, std::forward<Tail>(tail)...);
    }
}

template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
    if constexpr (sizeof...(Tail) > 0) {
        return head.size() + TotalSize(tail...);
    } else {
        return head.size();
    }
}

}  // namespace internal

/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
    size_t totalSize = internal::TotalSize(head, tail...);
    std::vector<typename std::decay_t<Head>::value_type> result;
    result.reserve(totalSize);
    internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
    return result;
}

1

Se quello che stai cercando è un modo per aggiungere un vettore a un altro dopo la creazione, vector::insertè la soluzione migliore, come è stato risposto più volte, ad esempio:

vector<int> first = {13};
const vector<int> second = {42};

first.insert(first.end(), second.cbegin(), second.cend());

Purtroppo non c'è modo di costruire un const vector<int>, come sopra devi costruire e poi insert.


Se quello che stai effettivamente cercando è un contenitore per contenere la concatenazione di questi due vector<int>s, potrebbe esserci qualcosa di meglio a tua disposizione se:

  1. Il tuo vector contiene primitivi
  2. Le tue primitive contenute hanno dimensioni di 32 bit o inferiori
  3. Vuoi un constcontenitore

Se quanto sopra è tutto vero, suggerirei di usare basic_stringchi char_typecorrisponde alla dimensione della primitiva contenuta nel tuo vector. Dovresti includere a static_assertnel tuo codice per confermare che queste dimensioni rimangano coerenti:

static_assert(sizeof(char32_t) == sizeof(int));

Con questa affermazione vera puoi semplicemente fare:

const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());

Per ulteriori informazioni sulle differenze tra stringe vectorpuoi consultare qui: https://stackoverflow.com/a/35558008/2642059

Per un esempio dal vivo di questo codice puoi consultare qui: http://ideone.com/7Iww3I


0

Questa soluzione potrebbe essere un po 'complicata, ma boost-rangeha anche alcune altre cose interessanti da offrire.

#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    boost::copy(b, std::back_inserter(a));
    for (auto& iter : a) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

Spesso l'intenzione è quella di combinare il vettore ae biterarlo semplicemente facendo qualche operazione. In questo caso, c'è la ridicola joinfunzione semplice .

#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>

int main(int, char**) {
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b = { 4,5,6 };
    std::vector<int> c = { 7,8,9 };
    // Just creates an iterator
    for (auto& iter : boost::join(a, boost::join(b, c))) {
        std::cout << iter << " ";
    }
    std::cout << "\n";
    // Can also be used to create a copy
    std::vector<int> d;
    boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
    for (auto& iter : d) {
        std::cout << iter << " ";
    }
    return EXIT_SUCCESS;
}

Per i vettori di grandi dimensioni questo potrebbe essere un vantaggio, in quanto non vi è alcuna copia. Può anche essere usato per copiare un generalizzato facilmente su più di un contenitore.

Per qualche ragione non c'è niente di simile boost::join(a,b,c), che potrebbe essere ragionevole.


0

Puoi farlo con algoritmi STL pre-implementati usando un modello per un uso di tipo polimorfico.

#include <iostream>
#include <vector>
#include <algorithm>

template<typename T>

void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){

     for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}

int main()
{
    std::vector<int> values_p={1,2,3,4,5};
    std::vector<int> values_s={6,7};

   concat(values_p, values_s);

    for(auto& it : values_p){

        std::cout<<it<<std::endl;
    }

    return 0;
}

È possibile cancellare il secondo vettore se non si desidera utilizzarlo ulteriormente ( clear()metodo).


-1

Concatena due std::vector-scon il forloop in unostd::vector .

Esempio:

std::vector <int> v1 {1, 2, 3};//declare vector1
std::vector <int> v2 {4, 5};//declare vector2
std::vector <int> suma;//declare vector suma

for(int i = 0; i < v1.size();i++)//for loop 1
{
     suma.push_back(v1[i]);
}

for(int i = 0; i< v2.size();i++)/for loop 2
{
     suma.push_back(v2[i]);
}

for(int i = 0; i < suma.size(); i++)//for loop 3-output
{
     std::cout<<suma[i];
}

Scrivi questo codice in main().


I forloop sono sbagliati. Gli indici validi in un vettore vanno da 0 a size()-1. Le condizioni di terminazione del loop devono essere i < v1.size(), usando <no <=. L'uso della condizione errata accede alla memoria all'esterno del contenitore.
Blastfurnace,

oltre a non funzionare, questo codice è fortemente non idiomatico. Dovresti almeno usare autoiteratori invece di indicizzazione manuale. Non ti importa di quale indice stai concatenando solo che è fatto in sequenza.
Tarick Welling,

Puoi spiegare perché stai usando size()-1in due delle tue condizioni di loop? Questo salta gli ultimi elementi vettoriali. Il terzo ciclo è l'unico corretto ora.
Blastfurnace

-3

Ad essere onesti, potresti concatenare velocemente due vettori copiando elementi da due vettori nell'altro o semplicemente aggiungendo solo uno dei due vettori !. Dipende dal tuo obiettivo.

Metodo 1: assegnare un nuovo vettore con le sue dimensioni è la somma delle dimensioni di due vettori originali.

vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector

Metodo 2: aggiungere il vettore A aggiungendo / inserendo elementi del vettore B.

// Loop for insert elements of vector_B into vector_A with insert() 
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());

4
Cosa aggiunge la tua risposta che non è già stata fornita in altre risposte?
Mat

13
@Mat: caratteri in grassetto.
marcv81,

Se i vettori originali non sono più necessari dopo, potrebbe essere meglio usarli in std::move_iteratormodo che gli elementi vengano spostati anziché copiati. (vedi en.cppreference.com/w/cpp/iterator/move_iterator ).
luglio

Che cosa è setcapacity? Che cosa è function: ?
LF

@LF Penso che stia parlando del resizemetodo.
Matthieu H,
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.