Concatenando dos std :: vectores

687

¿Cómo concateno dos std::vectors?

emlai
fuente
55
Las respuestas dadas en realidad no se concatenan. Anexan una copia. Puede ser útil (para el punto de vista de la eficiencia) crear un método de concatenación std :: vector, sin embargo, requeriría un intercambio sofisticado de la gestión de los nodos y probablemente es por eso que no se ha hecho.
FauChristian
8
@FauChristian: No, puede que no haya un uso desde el punto de vista de la eficiencia. La memoria del vector debe ser continua, por lo que lo sugerido es imposible. Si desea "un intercambio sofisticado de la gestión de los nodos", y si fuera a cambiar la clase de vectores de esa manera, terminaría con una disminución. Aun así, es muy difícil reutilizar la memoria de la manera sugerida, aunque comenzaría a ser un poco más factible. No creo que esté implementado actualmente. Lo principal es que al compartir tales nodos de administración (una deque), el nodo final podría estar parcialmente vacío.
Cookie
44
@lecaruyer Te das cuenta de que acabas de marcar una pregunta que se hizo dos años antes como un duplicado
eshirima
99
¿Soy el único que se pregunta por qué esto no se implementa como a + bo a.concat(b)en la biblioteca estándar? Tal vez la implementación predeterminada sería subóptima, pero no es necesario que cada concatenación de matriz sea micro-optimizada
oseiskar
99
años de evolución, la sobrecarga de operadores más avanzada de cualquier lenguaje convencional, un sistema de plantillas que duplica la complejidad del lenguaje y, sin embargo, la respuesta no es v = v1 + v2;
Spike0xff

Respuestas:

727
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
Robert Gamble
fuente
53
Solo agregaría código para obtener primero el número de elementos que contiene cada vector, y establecería que vector1 sea el que tenga el mayor. Si no lo hace, está haciendo muchas copias innecesarias.
Joe Pineda
34
Tengo una pregunta. ¿Funcionará esto si vector1 y vector2 son los mismos vectores?
Alexander Rafferty
66
Si ha concatenado varios vectores en uno, ¿es útil llamar reserveprimero al vector de destino?
Faheem Mitha
33
@AlexanderRafferty: solo si vector1.capacity() >= 2 * vector1.size(). Lo cual es atípico a menos que haya llamado std::vector::reserve(). De lo contrario, el vector se reasignará, invalidando los iteradores pasados ​​como parámetros 2 y 3.
Drew Dormann
28
Es una pena que no haya una expresión más sucinta en la biblioteca estándar. .concato +=algo así
RMN
193

Si está utilizando C ++ 11 y desea mover los elementos en lugar de simplemente copiarlos, puede usar std::move_iteratorjunto con insertar (o copiar):

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

Esto no será más eficiente para el ejemplo con ints, ya que moverlos no es más eficiente que copiarlos, pero para una estructura de datos con movimientos optimizados, puede evitar copiar estados innecesarios:

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

Después del movimiento, el elemento src se deja en un estado indefinido pero seguro para destruir, y sus elementos anteriores se transfirieron directamente al nuevo elemento de dest al final.

Alex
fuente
66
El método std :: make_move_iterator () me ayudó cuando intenté concatenar std :: vectores de std :: unique_ptr.
Knitschi
¿Cuál es la diferencia entre esto y std::move(src.begin(), src.end(), back_inserter(dest))?
kshenoy
145

Me gustaría utilizar la función de inserción , algo así como:

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Tom Ritter
fuente
77

O podrías usar:

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

Este patrón es útil si los dos vectores no contienen exactamente el mismo tipo de cosa, porque puede usar algo en lugar de std :: back_inserter para convertir de un tipo a otro.

Roger Lipscombe
fuente
77
El método de copia no es una buena manera. Llamará a push_back varias veces, lo que significa que si se deben insertar muchos elementos, esto podría significar múltiples reasignaciones. es mejor usar insert ya que la implementación del vector podría hacer alguna optimización para evitar reasignaciones. podría reservar memoria antes de comenzar a copiar
Yogesh Arora
77
@Yogesh: concedido, pero no hay nada que te impida llamar reserveprimero. La razón a std::copyveces es útil si desea utilizar algo distinto de back_inserter.
Roger Lipscombe
Cuando dice "asignaciones múltiples", eso es cierto, pero el número de asignaciones es en el peor registro (número de entradas agregadas), lo que significa que el costo de agregar una entrada es constante en el número de entradas agregadas. (Básicamente, no se preocupe por eso a menos que el perfil muestre que necesita una reserva).
Martin Bonner apoya a Monica el
Es posible que desee utilizar std :: transform para hacer esto en su lugar.
Martin Broadhurst
1
copia apesta mucho, incluso con reserva. vector :: insert evitará todas las comprobaciones: quick-bench.com/bLJO4OfkAzMcWia7Pa80ynwmAIA
Denis Yaroshevskiy
63

Con C ++ 11, preferiría seguir para agregar el vector b a:

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

cuando ay bno se superponen, y bya no se va a usar.


Esto es std::movede <algorithm>, no el habitual std::move de <utility>.

Deqing
fuente
10
Comportamiento indefinido si a en realidad es b (lo cual está bien si sabe que eso nunca puede suceder, pero vale la pena tenerlo en cuenta en el código de propósito general).
Martin Bonner apoya a Monica el
1
@ MartinBonner Gracias por mencionar eso. Probablemente debería volver a la vieja insertforma que es más segura.
Deqing
15
Ah, el OTRO std :: move. Bastante confuso la primera vez que lo ves.
xaxxon
1
¿Es esto diferente de insert()con move_iterators? ¿Si es así, cómo?
GPhilo
1
He agregado una nota sobre lo std::moveque estamos hablando aquí, ya que la mayoría de las personas no conocen esta sobrecarga. Espero que sea una mejora.
YSC
38
std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());
James Curran
fuente
24

Prefiero uno que ya se menciona:

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

Pero si usa C ++ 11, hay una forma más genérica:

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

Además, no es parte de una pregunta, pero es aconsejable usar reserveantes de agregar para un mejor rendimiento. Y si está concatenando un vector consigo mismo, sin reservarlo falla, por lo que siempre debería hacerlo reserve.


Básicamente lo que necesitas:

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());
}
ST3
fuente
2
std::se deduce mediante una búsqueda dependiente de argumentos . end(a)será suficiente.
Asu
44
@Asu ADL solo se agregará std::si el tipo de aproviene std, lo que anula el aspecto genérico.
Potatoswatter
buen punto. en este caso es un vector, por lo que funcionaría de todos modos, pero sí, esa es una mejor solución.
Asu
std :: begin () / end () se agregaron para colecciones (como matrices) que no las tienen como funciones miembro. Pero las matrices tampoco tienen una función miembro insert (), y hace la pregunta "¿Hay una colección con insert () pero sin begin () (que funciona con std :: begin ())?"
James Curran
12

Deberías usar vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
Boris
fuente
2
¿No es lo mismo con la respuesta dada por Tom Ritter y Robert Gamble en 2008?
IgNite
9

Un aumento de rendimiento general para concatenar es verificar el tamaño de los vectores. E fusionar / insertar el más pequeño con el más 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());
}
Vikramjit Roy
fuente
¡Tan simple, pero nunca lo pensé de esa manera!
Zimano
2
El código de muestra es incorrecto. v1.insert(v2.end()...está utilizando un iterador en v2para especificar la posición en v1.
David Stone
También puede usar un intercambio rápido. @DavidStone Lo edité para que se pueda cambiar el orden de concat. ¿Es posible agregar al comienzo de un vector?
qwr
Puede insertar al principio, pero será más lento. Sin embargo, para realmente "concatenar", el orden generalmente importa, así que eso es lo que debe hacer.
David Stone
7

Si desea poder concatenar vectores de forma concisa, puede sobrecargar el +=operador.

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

Entonces puedes llamarlo así:

vector1 += vector2;
Daniel Giger
fuente
6

Si está interesado en una garantía de excepción fuerte (cuando el constructor de copias puede lanzar una excepción):

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

Similar append_movecon una fuerte garantía no se puede implementar en general si el constructor de movimiento del elemento vector puede lanzar (lo cual es poco probable pero aún así).

AlexT
fuente
¿No es posible v1.erase(...lanzar también?
Clase Esqueleto
insertYa maneja esto. Además, esta llamada a erasees equivalente a a resize.
Potatoswatter
Me gusta esto, ¡gracias!
natersoz
5

Agregue este a su archivo de encabezado:

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

y úsalo de esta manera:

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 contendrá [1,2,62]

Stepan Yakovenko
fuente
No sé por qué esto fue rechazado. Puede que no sea la forma más eficiente de hacerlo, pero no está mal y es efectivo.
leeor_net
4

Aquí hay una solución de uso general que utiliza semántica de movimiento 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);
}

Observe cómo esto difiere de appending a a vector.

Daniel
fuente
4

Puede preparar su propia plantilla para el operador +:

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

Lo siguiente: solo usa +:

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

Este ejemplo da salida:

1 2 3 4 5 6 7 8
Vladimir U.
fuente
2
Usar T operator+(const T & a, const T & b)es peligroso, es mejor usarlo vector<T> operator+(const vector<T> & a, const vector<T> & b).
Matthieu H
4

Existe un algoritmo std::mergede C ++ 17 , que es muy fácil de usar,

A continuación se muestra el ejemplo:

#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;
}
Pavan Chandaka
fuente
2
No creo que sea más fácil de usar que std::vector::insert, pero hace algo diferente: fusionar dos rangos en un nuevo rango versus insertar un vector al final de otro. Vale la pena mencionar en la respuesta?
jb
4

Si su objetivo es simplemente iterar sobre el rango de valores para fines de solo lectura, una alternativa es envolver ambos vectores alrededor de un proxy (O (1)) en lugar de copiarlos (O (n)), para que se vean rápidamente como una sola, contigua.

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

Consulte https://stackoverflow.com/a/55838758/2379625 para obtener más detalles, incluida la implementación 'VecProxy', así como los pros y los contras.

Ronald Souza
fuente
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));
ejemplo
fuente
66
Si bien este fragmento de código puede resolver el problema, no explica por qué o cómo responde la pregunta. Por favor, incluya una explicación de su código , como que realmente ayuda a mejorar la calidad de su puesto. Señaladores / revisores: para respuestas de solo código, como esta, voto negativo, ¡no las borre! (Nota: esta respuesta en realidad puede ser lo suficientemente simple como para hacer una explicación, y por lo tanto votos negativos, innecesarios. Es posible que aún desee agregar una explicación para evitar más banderas NAA / VLQ.)
Scott Weldon
2

He implementado esta función que concatena cualquier cantidad de contenedores, pasando de rvalue-references y copiando lo contrario

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;
}
Dibujó
fuente
1

Si lo que está buscando es una forma de agregar un vector a otro después de la creación, vector::insertes su mejor opción, como se ha respondido varias veces, por ejemplo:

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

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

Lamentablemente no hay forma de construir un const vector<int>, como se debe construir arriba y luego insert.


Si lo que realmente está buscando es un contenedor para contener la concatenación de estos dos vector<int>s, puede haber algo mejor disponible para usted, si:

  1. Tu vectorcontiene primitivas
  2. Sus primitivas contenidas son de 32 bits o menos
  3. Quieres un constcontenedor

Si todo lo anterior es cierto, te sugiero que uses basic_stringquién char_typecoincide con el tamaño de la primitiva contenida en tu vector. Debe incluir un static_asserten su código para validar que estos tamaños se mantengan consistentes:

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

Con esto es cierto, puedes hacer:

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

Para obtener más información sobre las diferencias entre stringy vectorpuede mirar aquí: https://stackoverflow.com/a/35558008/2642059

Para ver un ejemplo en vivo de este código, puede consultar aquí: http://ideone.com/7Iww3I

Jonathan Mee
fuente
0

Esta solución puede ser un poco complicada, pero boost-rangetambién tiene otras cosas buenas que ofrecer.

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

A menudo, la intención es combinar el vector a y bsimplemente iterar sobre él haciendo alguna operación. En este caso, existe la ridícula joinfunción simple .

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

Para vectores grandes, esto podría ser una ventaja, ya que no hay copia. También se puede usar para copiar y generalizar fácilmente en más de un contenedor.

Por alguna razón no hay nada como boost::join(a,b,c), lo que podría ser razonable.

Aleph0
fuente
0

Puede hacerlo con algoritmos STL pre-implementados usando una plantilla para un uso de tipo polimórfico.

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

Puede borrar el segundo vector si no desea usarlo más ( clear()método).

rekkalmd
fuente
-3

Para ser honesto, ¡podría concatenar rápidamente dos vectores copiando elementos de dos vectores en el otro o simplemente agregar uno de los dos vectores! Depende de tu puntería.

Método 1: asignar un nuevo vector con su tamaño es la suma del tamaño de dos vectores originales.

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

Método 2: Agregue el vector A agregando / insertando elementos del 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());
nvnhcmus
fuente
44
¿Qué agrega su respuesta que aún no se ha proporcionado en otras respuestas?
Mat
13
@ Mat: caracteres en negrita.
marcv81
Si los vectores originales ya no son necesarios después, puede ser mejor usarlos std::move_iteratorpara que los elementos se muevan en lugar de copiarse. (ver en.cppreference.com/w/cpp/iterator/move_iterator ).
tmlen
¿Qué es setcapacity? ¿Qué es function: ?
LF
@LF Creo que está hablando sobre el resizemétodo.
Matthieu H