Concaténation de deux vecteurs std :: vecteurs

Comment concaténer deux std :: vector s?

469
@lecaruyer Vous réalisez que vous venez de marquer une question posée deux ans auparavant en double
ajouté l'auteur eshirima, source
@FauChristian: Non, il n'y a peut-être pas d'utilisation du point de vue de l'efficacité. La mémoire de vecteur doit être continue, ce qui vous est suggéré est donc impossible. Si vous vouliez «un partage sophistiqué de la gestion des nœuds», et si vous deviez modifier la classe de vecteurs de cette manière, vous obtiendriez une deque. Même alors, il est très difficile de réutiliser la mémoire de la manière suggérée, même si cela commence à être un peu plus réalisable. Je ne pense pas qu'il est actuellement mis en œuvre. L'essentiel est que dans un tel partage des noeuds de gestion (un deque)
ajouté l'auteur Cookie, source
Les réponses données ne concaténent pas réellement. Ils ajoutent une copie. Il peut être utile (pour le point de vue de l'efficacité) de créer une méthode de concaténation std :: vector, mais cela nécessiterait un partage sophistiqué de la gestion des nœuds et c'est probablement pourquoi cela n'a pas été fait.
ajouté l'auteur FauChristian, source
Suis-je le seul à me demander pourquoi cela n'est pas implémenté comme a + b ou a.concat (b) dans la bibliothèque standard? Peut-être que l'implémentation par défaut serait sous-optimale, mais chaque concaténation de tableau n'a pas besoin d'être micro-optimisée
ajouté l'auteur oseiskar, source

16 Réponses

vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
531
ajouté
Si vous avez concaténé plusieurs vecteurs à un, est-il utile d'appeler reserve sur le vecteur de destination en premier?
ajouté l'auteur Faheem Mitha, source
@Aidin je vois. Merci pour la clarification.
ajouté l'auteur Faheem Mitha, source
@Khaur: C'est horrible au point que c'est difficile à croire. C'est super trivial pour une implémentation de détecter les itérateurs sont un accès aléatoire, calculer la taille, et pré-réserver l'espace nécessaire. Je pense que MSFT le fait même pour les itérateurs avancés.
ajouté l'auteur Mooing Duck, source
@AlexanderRafferty: Uniquement si vector1.capacity ()> = 2 * vector1.size() . Ce qui est atypique sauf si vous avez appelé std :: vector :: reserve() . Sinon, le vecteur va se réallouer, invalidant les itérateurs passés en paramètres 2 et 3.
ajouté l'auteur Drew Dormann, source
J'ai une question. Est-ce que cela fonctionnera si vector1 et vector2 sont les mêmes vecteurs?
ajouté l'auteur Alexander Rafferty, source
Je n'ajouterais que du code pour obtenir d'abord le nombre d'éléments que chaque vecteur contient, et définir vector1 comme étant le plus grand. Si vous faites autrement, vous faites beaucoup de copies inutiles.
ajouté l'auteur Joe Pineda, source
C'est dommage qu'il n'y ait pas d'expression plus succincte dans la bibliothèque standard. .concat ou + = ou quelque chose
ajouté l'auteur nmr, source
@FaheemMitha: Puisque les arguments de insert sont des vecteurs, on sait déjà combien d'éléments sont devant et vont le gérer lui-même. Si nous insérions d'autres choses comme array, il était utile de réserver l'espace en premier.
ajouté l'auteur Aidin, source
@MooingDuck Vous avez raison, j'ai manqué le répartiteur et j'ai pensé que la version de l'itérateur d'entrée s'appliquait à tous les types d'itérateurs. La version de l'itérateur vers l'avant fait beaucoup plus de choses. Merci de l'avoir signalé, j'ai supprimé mon commentaire initial pour qu'il n'apparaisse pas sans le vôtre.
ajouté l'auteur Khaur, source

J'utiliserais la fonction d'insertion , quelque chose comme:

vector a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
112
ajouté

Si vous utilisez C ++ 11, et que vous souhaitez déplacer les éléments plutôt que de simplement les copier, vous pouvez utiliser std :: move_iterator ( http://fr.cppreference.com/w/cpp/iterator/move_iterator ) avec insertion (ou copie):

#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;
}

Cela ne sera pas plus efficace pour l'exemple avec ints, car les déplacer n'est pas plus efficace que de les copier, mais pour une structure de données avec des déplacements optimisés, il est possible d'éviter de copier un état 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;
}

Après le déplacement, l'élément de src est laissé dans un état indéfini mais sûr à détruire, et ses anciens éléments ont été transférés directement au nouvel élément de dest à la fin.

112
ajouté
La méthode std :: make_move_iterator() m'a aidé lors de la concaténation de std :: vectors de std :: unique_ptr.
ajouté l'auteur Knitschi, source

Ou vous pouvez utiliser:

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

Ce modèle est utile si les deux vecteurs ne contiennent pas exactement le même type de chose, car vous pouvez utiliser quelque chose à la place de std :: back_inserter pour convertir un type en un autre.

70
ajouté
@Yogesh: accordé, mais rien ne vous empêche d'appeler reserve en premier. La raison std :: copy est parfois utile si vous voulez utiliser autre chose que back_inserter .
ajouté l'auteur Roger Lipscombe, source
Quand vous dites "allocations multiples", c'est vrai - mais le nombre d'allocations est au pire log (nombre d'entrées ajoutées) - ce qui signifie que le coût d'ajout d'une entrée est constant dans le nombre d'entrées ajoutées. (Fondamentalement, ne vous inquiétez pas à moins que le profilage montre que vous avez besoin d'une réserve).
ajouté l'auteur Martin Bonner, source
Vous pourriez vouloir utiliser std :: transform pour le faire à la place.
ajouté l'auteur Martin Broadhurst, source
la méthode de copie n'est pas un si bon moyen. Il appellera push_back plusieurs fois ce qui signifie que si beaucoup d'éléments doivent être insérés cela pourrait signifier plusieurs réallocations. il est préférable d'utiliser insert car l'implémentation vectorielle pourrait faire une certaine optimisation pour éviter les réaffectations. il pourrait réserver de la mémoire avant de commencer à copier
ajouté l'auteur Yogesh Arora, source
std::vector first;
std::vector second;

first.insert(first.end(), second.begin(), second.end());
30
ajouté

Avec C ++ 11, je préférerais suivre pour ajouter le vecteur b à un:

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

quand a et b ne se chevauchent pas, et b ne sera plus utilisé.

27
ajouté
@ MartinBonner Merci d'avoir mentionné cela. Je devrais probablement revenir à l'ancienne façon insert qui est plus sûre.
ajouté l'auteur Deqing, source
ajoutez juste la ligne d'en-tête suivante le début: #include
ajouté l'auteur Manohar Reddy Poreddy, source
Comportement non défini si a est en fait b (ce qui est OK si vous savez que cela ne peut jamais arriver - mais vaut la peine d'en être conscient dans le code généraliste).
ajouté l'auteur Martin Bonner, source
Ah, l'autre std :: move. Très confus la première fois que vous le voyez.
ajouté l'auteur xaxxon, source

Je préfère celui qui est déjà mentionné:

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

Mais si vous utilisez C ++ 11, il existe un moyen plus générique:

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

Aussi, ne fait pas partie d'une question, mais il est conseillé d'utiliser reserve avant d'ajouter pour de meilleures performances. Et si vous concaténez un vecteur avec lui-même, sans le réserver, il faut toujours réserver .


Donc, fondamentalement, ce dont vous avez besoin:

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
ajouté
@Asu ADL n'ajoutera que std :: si le type de a provient de std , ce qui détruit l'aspect générique.
ajouté l'auteur Potatoswatter, source
std :: est déduit par recherche dépendant de l'argument . end (a) suffira.
ajouté l'auteur Asu, source
bon point. dans ce cas, c'est un vecteur donc ça marcherait de toute façon, mais oui c'est une meilleure solution.
ajouté l'auteur Asu, source

Vous devriez utiliser vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
5
ajouté

Si vous êtes intéressé par une garantie d'exception forte (lorsque le constructeur de la copie peut lancer une exception):

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;
    }
}

Un append_move similaire avec une forte garantie ne peut pas être implémenté en général si le constructeur de déplacement de l'élément vectoriel peut lancer (ce qui est peu probable mais toujours).

4
ajouté
insert gère déjà cela. En outre, cet appel à erase est équivalent à resize .
ajouté l'auteur Potatoswatter, source
Est-ce que ce n'est pas possible pour v1.erase (... de lancer aussi?
ajouté l'auteur camelCase, source
vector v1 = {1, 2, 3, 4, 5};
vector v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
3
ajouté
Bien que cet extrait de code puisse résoudre le problème, il n'explique ni pourquoi ni comment il répond à la question. Veuillez inclure une explication pour votre code , car cela contribue réellement à améliorer la qualité de votre message. Marqueurs/réviseurs: Pour les réponses à code uniquement comme celui-ci, downvote, ne supprimez pas! (Note: Cette réponse peut être assez simple pour rendre une explication, et donc des sous-évaluations, inutile Vous pouvez toujours vo
ajouté l'auteur Scott Weldon, source

Ajoutez celui-ci à votre fichier d'en-tête:

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;
}

et utilisez-le de cette façon:

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

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

vector r = concat(a, b);

r contiendra [1,2,62]

2
ajouté
Je ne sais pas pourquoi cela a été voté. Ce n'est peut-être pas la façon la plus efficace de le faire, mais ce n'est pas faux et c'est efficace.
ajouté l'auteur leeor_net, source

Avec range v3 , vous pouvez avoir une concaténation paresseuse :

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

Demo.

2
ajouté

Voici une solution générale utilisant la sémantique de déplacement 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);
}

Notez en quoi cela diffère de append par un vector .

1
ajouté

Si ce que vous cherchez est un moyen d'ajouter un vecteur à un autre après la création, vector :: insert est votre meilleur pari, comme cela a été répondu plusieurs fois, par exemple:

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. Votre vector contient des primitives
  2. Vos primitives contenues sont de taille 32 bits ou moins
  3. Vous voulez un conteneur const

Si tout ce qui précède est vrai, je suggère d'utiliser le basic_string Qui char_type correspond à la taille de la primitive contenue dans votre vector . Vous devez inclure un static_assert dans votre code pour valider ces tailles restent cohérentes:

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

Avec cette attente, vous pouvez juste faire:

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
ajouté

Pour être honnête, vous pouvez rapidement concaténer deux vecteurs en copiant des éléments de deux vecteurs dans l'autre ou simplement ajouter un seul des deux vecteurs! Cela dépend de votre objectif.

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
ajouté
Qu'ajoute votre réponse qui n'a pas déjà été fournie dans d'autres réponses?
ajouté l'auteur Mat, source
Si le (s) vecteur (s) d'origine n'est plus nécessaire après, il peut être préférable d'utiliser std :: move_iterator pour que les éléments soient déplacés au lieu d'être copiés. (voir fr.cppreference.com/w/cpp/iterator/move_iterator ) .
ajouté l'auteur tmlen, source
@Mat: caractères gras.
ajouté l'auteur marcv81, source

Une amélioration générale des performances pour concaténer consiste à vérifier la taille des vecteurs. Et fusionnez/insérez le plus petit avec le plus grand.

//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
ajouté