Concatenarea a două std :: vectori

Cum pot concatena două std :: vector s?

469
@lecaruyer Vă dați seama că tocmai ați marcat o întrebare care a fost cerută cu doi ani în urmă ca duplicat
adăugat autor eshirima, sursa
@FauChristian: Nu, este posibil să nu existe o utilizare din punctul de vedere al eficienței. Vectorul de memorie trebuie să fie continuu, deci ceea ce vă este sugerat este imposibil. Dacă ați vrut "o partajare sofisticată a gestionării nodurilor" și dacă ați schimba clasa vectorilor într-un astfel de mod, veți ajunge la un deque. Chiar și atunci este foarte dificilă reutilizarea memoriei în modul sugerat, deși ar începe să fie ceva mai fezabil. Nu cred că este implementat în prezent. Principalul lucru este că într-o astfel de partajare a nodurilor de management (un deque) nodul final ar putea
adăugat autor Cookie, sursa
Răspunsurile date nu se reunesc. Se anexează o copie. Poate exista o folosire (din punctul de vedere al eficientei) pentru a crea o metoda std :: vector concatenate, cu toate acestea ar fi nevoie de o partajare sofisticata a managementului nodurilor si probabil asta nu a fost facuta.
adăugat autor FauChristian, sursa
Sunt singurul care se întreabă de ce acest lucru nu este implementat ca a + b sau a.concat (b) în biblioteca standard? Poate că implementarea implicită ar fi suboptimală, dar nu este nevoie ca fiecare concatenare a matricei să fie micro-optimizată
adăugat autor oseiskar, sursa

16 răspunsuri

vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
531
adăugat
Dacă ați concatenat mai multe vectori la unul, este de dorit mai întâi să apelați reserve pe vectorul de destinație?
adăugat autor Faheem Mitha, sursa
@ Aidin văd. Multumesc pentru clarificare.
adăugat autor Faheem Mitha, sursa
@ Khaur: Este groaznic până la punctul în care e greu de crezut. Este foarte banal pentru o implementare pentru a detecta iteratorii care au acces la întâmplare, pentru a calcula dimensiunea și a rezerva în prealabil spațiul necesar. Cred că MSFT face asta chiar și pentru iteratori în față.
adăugat autor Mooing Duck, sursa
@AlexanderRafferty: Doar dacă vector1.capacity ()> = 2 * vector1.size() . Care este atipic dacă nu ați sunat std :: vector :: reserve() . În caz contrar, vectorul va realoca, invalidând iteratorii parcurși ca parametrii 2 și 3.
adăugat autor Drew Dormann, sursa
Am o intrebare. Va funcționa acest lucru dacă vector1 și vector2 sunt aceiași vectori?
adăugat autor Alexander Rafferty, sursa
Aș adăuga doar codul pentru a obține mai întâi numărul de elemente deținute de fiecare vector și setați vectorul1 pentru a fi cel care deține cel mai mare număr. Dacă faceți altfel, faceți o copie inutilă.
adăugat autor Joe Pineda, sursa
Este prea rău că nu există o expresie mai succintă în biblioteca standard. .concat sau + = sau ceva
adăugat autor nmr, sursa
@FaheemMitha: Deoarece argumentele insert sunt vectori, deja știți câte elemente sunt în față și se vor ocupa de ele însele. Dacă introducem alte lucruri cum ar fi matricea, a fost util să rezervăm mai întâi spațiul.
adăugat autor Aidin, sursa
@MooingDuck Ai dreptate, am ratat dispecerul și am crezut că versiunea iterator de intrare a fost aplicată pentru toate tipurile de iteratori. Versiunea cu iterator înainte face mult mai multe lucruri. Vă mulțumim pentru că ați arătat acest lucru, că mi-am șters comentariul inițial, astfel încât să nu apară fără al dumneavoastră.
adăugat autor Khaur, sursa

Aș folosi funcția de inserare , cum ar fi:

vector a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
112
adăugat

Dacă utilizați C ++ 11 și doriți să mutați elementele mai degrabă decât să le copiați, puteți folosi std :: move_iterator ( http://ro.cppreference.com/w/cpp/iterator/move_iterator ) împreună cu inserați (sau copiați):

#include 
#include 
#include 

int main(int argc, char** argv) {
  std::vector dest{1,2,3,4,5};
  std::vector 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(std::cout, "\n")
    );

  return 0;
}

Acest lucru nu va fi mai eficient pentru exemplul cu inturi, deoarece mutarea acestora nu este mai eficientă decât copierea, ci pentru o structură de date cu mișcări optimizate, poate evita copierea stării inutile:

#include 
#include 
#include 

int main(int argc, char** argv) {
  std::vector> dest{{1,2,3,4,5}, {3,4}};
  std::vector> 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;
}

După mutare, elementul lui src este lăsat într-o stare nedefinită, dar în condiții de siguranță, iar elementele sale anterioare au fost transferate direct la elementul nou al lui Dest.

112
adăugat
Metoda std :: make_move_iterator() ma ajutat la încercarea de a concatena std :: vectori ai std :: unique_ptr.
adăugat autor Knitschi, sursa

Sau puteți folosi:

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

Acest model este util dacă cele două vectori nu conțin exact același tip de lucru, deoarece puteți utiliza ceva în loc de std :: back_inserter pentru a converti de la un tip la altul.

70
adăugat
@Yogesh: acordat, dar nu există nimic care să vă oprească mai întâi să apelați reserve . Motivul std :: copy este uneori util dacă doriți să utilizați altceva decât back_inserter .
adăugat autor Roger Lipscombe, sursa
Când spui "alocări multiple", este adevărat - dar numărul de alocări este cel mai rău în log (numărul de intrări adăugate) - ceea ce înseamnă că costul adăugării unei intrări este constant în numărul de intrări adăugate. (Practic, nu vă faceți griji dacă profilul nu arată că aveți nevoie de o rezervă).
adăugat autor Martin Bonner, sursa
S-ar putea să doriți să utilizați std :: transform pentru a face asta în schimb.
adăugat autor Martin Broadhurst, sursa
metoda de copiere nu este o modalitate atât de bună. Acesta va numi push_back mai mult timp, ceea ce înseamnă că, dacă trebuie introduse o mulțime de elemente, aceasta ar putea însemna mai multe realocări. este mai bine să utilizați insertul deoarece implementarea vectorului ar putea face unele optimizări pentru a evita realocările. ar putea rezerva memorie înainte de a începe copierea
adăugat autor Yogesh Arora, sursa
std::vector first;
std::vector second;

first.insert(first.end(), second.begin(), second.end());
30
adăugat

Cu C ++ 11, aș prefera să adăugați vectorul b la a:

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

când a și b nu sunt suprapuse și b nu va mai fi folosit.

27
adăugat
@MartinBonner Vă mulțumim că ați menționat acest lucru. Probabil ar trebui să mă întorc la vechiul mod insert care este mai sigur.
adăugat autor Deqing, sursa
adăugați doar următoarea linie antet începe: #include
adăugat autor Manohar Reddy Poreddy, sursa
Comportamentul nedefinit dacă este de fapt b (ceea ce este OK dacă știi că nu se poate întâmpla niciodată - dar merită să fii conștient de codul cu scop general).
adăugat autor Martin Bonner, sursa
Ah, ALTE std :: mișcă. Destul de confuză de prima dată când o vezi.
adăugat autor xaxxon, sursa

Prefer una care este deja menționată:

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

Dar dacă utilizați C ++ 11, există un alt mod generic:

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

De asemenea, nu face parte dintr-o întrebare, dar este recomandabil să utilizați reserve înainte de a fi adăugat pentru o performanță mai bună. Și dacă concatenați vectorul cu el însuși, fără să-l rezervați eșuează, deci trebuie întotdeauna să reserve .


Deci, de fapt, ceea ce aveți nevoie:

template 
void Append(std::vector& a, const std::vector& b)
{
    a.reserve(a.size() + b.size());
    a.insert(a.end(), b.begin(), b.end());
}
18
adăugat
@Asu ADL va adăuga numai std :: dacă tipul a provine de la std , ceea ce învinge aspectul generic.
adăugat autor Potatoswatter, sursa
std :: este dedusă prin căutare dependentă de argument . end (a) va fi de ajuns.
adăugat autor Asu, sursa
buna observatie. în acest caz este un vector, așa că ar funcționa oricum, dar da este o soluție mai bună.
adăugat autor Asu, sursa

Ar trebui să utilizați vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
5
adăugat

Dacă sunteți interesat (ă) de o garanție excepțională puternică (atunci când constructorul de copiere poate arunca o excepție):

template
inline void append_copy(std::vector& v1, const std::vector& 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;
    }
}

append_move similară cu garanție puternică nu poate fi pusă în aplicare în general dacă constructorul de mutare a vectorului poate arunca (ceea ce este puțin probabil, dar încă).

4
adăugat
insert se ocupă deja de acest lucru. De asemenea, acest apel pentru erase este echivalent cu resize .
adăugat autor Potatoswatter, sursa
Nu este posibil ca v1.erase (... să arunce prea?
adăugat autor camelCase, sursa
vector v1 = {1, 2, 3, 4, 5};
vector v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
3
adăugat
În timp ce acest fragment de cod poate rezolva problema, nu explică de ce și cum răspunde la întrebare. includeți o explicație pentru codul dvs. , deoarece aceasta vă ajută într-adevăr să îmbunătățiți calitatea postării dvs. Fragmente/recenzori: Nu răspundeți doar la răspunsurile cu cod, cum ar fi acesta, downvote, nu ștergeți! (Notă: Acest răspuns poate fi destul de simplu pentru a explica și, prin urmare, pentru a reduce prețul, este inutil. Încă puteți să adăugați o exp
adăugat autor Scott Weldon, sursa

Adăugați-o în fișierul cu antet:

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

și folosiți-o în felul următor:

vector a = vector();
vector b = vector();

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

vector r = concat(a, b);

r va conține [1,2,62]

2
adăugat
Nu știu de ce a fost votat. Este posibil să nu fie cel mai eficient mod de a face acest lucru, dar nu este greșit și este eficient.
adăugat autor leeor_net, sursa

Cu intervalul v3 , este posibil să aveți o concatenare leneșă :

ranges::view::concat(v1, v2)

Demo.

2
adăugat

Iată o soluție cu scop general care utilizează semantica de deplasare C ++ 11:

template 
std::vector concat(const std::vector& lhs, const std::vector& rhs)
{
    if (lhs.empty()) return rhs;
    if (rhs.empty()) return lhs;
    std::vector 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 
std::vector concat(std::vector&& lhs, const std::vector& rhs)
{
    lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
    return std::move(lhs);
}

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

template 
std::vector concat(std::vector&& lhs, std::vector&& 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);
}

Rețineți cum aceasta diferă de append la un vector .

1
adăugat

Dacă ceea ce căutați este o modalitate de a adăuga un vector la altul după creare, vector :: insert este cel mai bun pariu, așa cum sa răspuns de mai multe ori, de exemplu:

vector first = {13};
const vector second = {42};

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

Sadly there's no way to construct a const vector, as above you must construct and then insert.


If what you're actually looking for is a container to hold the concatenation of these two vectors, there may be something better available to you, if:

  1. Vectorul dvs. conține primitive
  2. Primele dvs. conținute sunt de dimensiune de 32 de biți sau mai mici
  3. Vrei un container const

Dacă cele de mai sus sunt toate adevărate, vă sugerăm să utilizați basic_string care este char_type se potrivește cu mărimea primitivului conținut în vectorul . Ar trebui să includeți un cod static_assert în validează aceste dimensiuni să rămână consecvente:

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

Cu această afirmație adevărată poți să faci:

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

For more information on the differences between string and vector you can look here: https://stackoverflow.com/a/35558008/2642059

For a live example of this code you can look here: http://ideone.com/7Iww3I

0
adăugat

Pentru a fi sincer, puteți concatena rapid două vectori prin copierea elementelor de la două vectori în celălalt sau doar adăugați unul din cele două vectori !. Depinde de scopul tău.

Method 1: Assign new vector with its size is the sum of two original vectors' size.

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

Method 2: Append vector A by adding/inserting elements of vector 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());
0
adăugat
În ce răspuns adaugă răspunsul dvs. care nu a fost deja furnizat în alte răspunsuri?
adăugat autor Mat, sursa
Dacă vectorul original nu mai este necesar, este mai bine să utilizați std :: move_iterator astfel încât elementele să fie mutate în loc să fie copiate. (consultați en.cppreference.com/w/cpp/iterator/move_iterator ) .
adăugat autor tmlen, sursa
@Mat: caractere aldine.
adăugat autor marcv81, sursa

O creștere generală a performanței pentru concatenare este de a verifica dimensiunea vectorilor. Și îmbinați/inserați cel mai mic cu cel mai mare.

//vector v1,v2;
if(v1.size()>v2.size()){
    v1.insert(v1.end(),v2.begin(),v2.end());
}else{
    v1.insert(v2.end(),v1.begin(),v1.end());
}
0
adăugat