Как объединить два std::vector
?
Объединение двух std :: vectors
Ответы (25)
Если вы используете C ++ 11 и хотите переместить элементы, а не просто их копировать, вы можете использовать _ 1_ вместе со вставкой (или копией):
#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;
}
Это не будет более эффективным для примера с целыми числами, поскольку их перемещение не более эффективно, чем их копирование, но для структуры данных с оптимизированными перемещениями можно избежать копирования ненужного состояния:
#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;
}
После перемещения элемент src остается в неопределенном, но безопасном для уничтожения состоянии, а его прежние элементы были перенесены непосредственно в новый элемент dest в конце.
std::move(src.begin(), src.end(), back_inserter(dest))
?
- person kshenoy; 25.02.2020
insert
может выделить необходимый объем памяти за один ход. Когда back_inserter
может привести к нескольким перераспределениям
- person yrHeTateJlb; 01.10.2020
Я бы использовал функцию вставки, например:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Или вы можете использовать:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Этот шаблон полезен, если два вектора не содержат в точности один и тот же тип объектов, потому что вы можете использовать что-то вместо std :: back_inserter для преобразования из одного типа в другой.
reserve
. Причина, по которой std::copy
иногда бывает полезна, заключается в том, что вы хотите использовать что-то другое, кроме back_inserter
.
- person Roger Lipscombe; 22.03.2010
using_resize
дважды записывает пространство памяти res
, должно быть медленнее ??
- person Samuel Li; 12.11.2020
if > capacity_
в push_back, если проблема. Достаточно большой проблемы, что memset
в resize
не имеет значения.
- person Denis Yaroshevskiy; 13.11.2020
В C ++ 11 я бы предпочел следующее, чтобы добавить вектор b к a:
std::move(b.begin(), b.end(), std::back_inserter(a));
когда a
и b
не перекрываются, и b
больше не будет использоваться.
Это std::move
из <algorithm>
, а не обычный _ 7_ от <utility>
.
insert
способу, который более безопасен.
- person Deqing; 01.02.2016
insert()
с move_iterator
s? Если да, то как?
- person GPhilo; 05.11.2018
std::move
мы здесь говорим, поскольку большинство людей не знают об этой перегрузке. Надеюсь, это улучшение.
- person YSC; 08.11.2018
Я предпочитаю тот, который уже упоминался:
a.insert(a.end(), b.begin(), b.end());
Но если вы используете C ++ 11, есть еще один общий способ:
a.insert(std::end(a), std::begin(b), std::end(b));
Кроме того, это не является частью вопроса, но рекомендуется использовать reserve
перед добавление для лучшей производительности. И если вы объединяете вектор с самим собой, без его резервирования не получается, поэтому всегда нужно reserve
.
Итак, в основном, что вам нужно:
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());
}
std::
выводится с помощью поиска, зависящего от аргументов. end(a)
будет достаточно.
- person Asu; 13.10.2016
std::
только в том случае, если тип a
исходит из std
, что нарушает общий аспект.
- person Potatoswatter; 27.12.2016
Общее повышение производительности для конкатенации - это проверка размера векторов. И объедините / вставьте меньший с большим.
//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());
}
v1.insert(v2.end()...
использует итератор в v2
, чтобы указать позицию в v1
.
- person David Stone; 06.07.2019
Если вы хотите иметь возможность кратко объединять векторы, вы можете перегрузить оператор +=
.
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;
}
Тогда вы можете назвать это так:
vector1 += vector2;
Вы должны использовать vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Если вас интересует строгая гарантия исключения (когда конструктор копирования может вызвать исключение):
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;
}
}
Подобный append_move
с сильной гарантией не может быть реализован в целом, если конструктор перемещения векторного элемента может бросать (что маловероятно, но все же).
v1.erase(...
кинуть нельзя?
- person Class Skeleton; 12.10.2015
insert
уже занимается этим. Кроме того, этот вызов erase
эквивалентен resize
.
- person Potatoswatter; 27.12.2016
Существует алгоритм std::merge
из C ++ 17 , который очень легко использовать при сортировке входных векторов,
Ниже приведен пример:
#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;
}
std::vector::insert
, но он имеет дело с чем-то другим: объединением двух диапазонов в новый диапазон против вставки одного вектора в конец другого. Стоит упомянуть в ответе?
- person j b; 05.12.2019
Добавьте это в свой заголовочный файл:
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;
}
и используйте его так:
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 будет содержать [1,2,62]
Если ваша цель - просто перебрать диапазон значений только для чтения, альтернатива - обернуть оба вектора вокруг прокси (O (1)) вместо их копирования (O (n)), чтобы они сразу были видны. как единый, непрерывный.
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
Обратитесь к https://stackoverflow.com/a/55838758/2379625 для получения дополнительных сведений, включая реализацию VecProxy как а также плюсы и минусы.
Вот решение общего назначения, использующее семантику перемещения 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);
}
Обратите внимание, чем это отличается от append
и vector
.
Вы можете подготовить собственный шаблон для оператора +:
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;
}
Следующее - просто используйте +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Этот пример дает результат:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
опасно, лучше использовать vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
- person Matthieu H; 13.12.2019
Я реализовал эту функцию, которая объединяет любое количество контейнеров, переходя от rvalue-ссылок и копируя в противном случае
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;
}
Это решение может быть немного сложным, но boost-range
может предложить и другие приятные вещи.
#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
и b
, просто перебирая его, выполняя некоторую операцию. В этом случае есть нелепая простая функция join
.
#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;
}
Для больших векторов это может быть преимуществом, поскольку отсутствует копирование. Его также можно использовать для простого копирования обобщений на более чем один контейнер.
Почему-то нет ничего похожего на boost::join(a,b,c)
, что могло бы быть разумным.
Для контейнеров, которые предлагают push_back
(string, vector, deque, ...):
std::copy(std::begin(input), std::end(input), std::back_inserter(output))
а также
для контейнеров, предлагающих insert
(карты, наборы):
std::copy(std::begin(input), std::end(input), std::inserter(output, output.end()))
Если вы ищете способ добавить один вектор к другому после создания, vector::insert
- ваш лучший выбор, о чем уже несколько раз говорили, например:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
К сожалению, нет способа построить const vector<int>
, как указано выше, вы должны построить, а затем insert
.
Если на самом деле вы ищете контейнер для объединения этих двух vector<int>
, возможно, вам будет доступно что-то получше, если:
- Ваш
vector
содержит примитивы - Ваши содержащиеся примитивы имеют размер 32-бит или меньше
- Вам нужен
const
контейнер
Если все вышесказанное верно, я бы предложил использовать basic_string
кто char_type
соответствует размеру примитива, содержащегося в вашем vector
. Вы должны включить static_assert
в свой код, чтобы убедиться, что эти размеры остаются неизменными:
static_assert(sizeof(char32_t) == sizeof(int));
С этим утверждением вы можете просто делать:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Дополнительную информацию о различиях между string
и vector
вы можете найти здесь: https://stackoverflow.com/a/35558008/2642059 а>
Живой пример этого кода можно посмотреть здесь: http://ideone.com/7Iww3I
Вы можете сделать это с помощью предварительно реализованных алгоритмов STL, используя шаблон для использования полиморфного типа.
#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;
}
Вы можете очистить второй вектор, если не хотите использовать его в дальнейшем (метод clear()
).
Соедините два std::vector-s
с for
циклом в один std::vector
.
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];
}
auto
итераторы вместо ручного индексирования. Вам не важно, какой индекс вы объединяете, только то, что это выполняется последовательно.
- person Tarick Welling; 08.06.2020
Попробуйте создать два вектора и добавить второй вектор к первому вектору, код:
std::vector<int> v1{1,2,3};
std::vector<int> v2{4,5};
for(int i = 0; i<v2.size();i++)
{
v1.push_back(v2[i]);
}
v1:1,2,3.
Описание:
Пока i int не имеет размера v2, отодвиньте элемент, индекс i в векторе v1.
Честно говоря, вы можете быстро объединить два вектора, скопировав элементы из двух векторов в другой или просто добавив только один из двух векторов !. Это зависит от вашей цели.
Метод 1: Назначьте новому вектору размер, равный сумме двух исходных размеров векторов.
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
Метод 2. Добавьте вектор A, добавив / вставив элементы вектора 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());
std::move_iterator
, чтобы элементы перемещались, а не копировались. (см. en.cppreference.com/w/cpp/iterator/move_iterator) .
- person tmlen; 23.02.2018
setcapacity
? Что такое function:
?
- person L. F.; 29.03.2019
resize
.
- person Matthieu H; 13.12.2019
reserve
для вектора назначения?
- person Faheem Mitha; 05.02.2012
vector1.capacity() >= 2 * vector1.size()
. Что нетипично, если вы не позвонили std::vector::reserve()
. В противном случае вектор будет перераспределен, что сделает недействительными итераторы, переданные как параметры 2 и 3.
- person Drew Dormann; 22.06.2012
insert
являются векторами, он уже знает, сколько элементов впереди, и обработает это сам. Если мы вставляли другие вещи, такие как массив, было бы полезно сначала зарезервировать место.
- person Aidin; 14.04.2014
.concat
или +=
или что-то в этом роде
- person nmr; 15.10.2016
std::vector::insert
также был опубликован в нескольких других ответах, но у него больше всего голосов, поэтому я спрошу / заявлю здесь: Использование insert
должно иметь преимущество перед другими подходами, которые используют back_inserter
, потому что insert
потенциально может использовать memcpy в одном массиве, в то время как подходы, использующие back_inserter
, должны перебирать вектор, который будет вставлен элемент за элементом. Я правильно понимаю?
- person j00hi; 22.08.2019
a + b
илиa.concat(b)
в стандартной библиотеке? Возможно, реализация по умолчанию будет неоптимальной, но каждое объединение массивов не требует микрооптимизации. - person oseiskar   schedule 24.03.2018forceVector1 + forceVector2
, чтобы выполнить поэлементное добавление в ясном и кратком коде. - person Jonathan Lidbeck   schedule 06.02.2020insert
) или перегрузку push back. - person Stefano Buora   schedule 17.02.2021