Конкатенация двух стандартных: векторов

508

Как объединить два std::vector s?

  • 4
    Приведенные ответы на самом деле не объединяются. Они добавляют копию. Может быть использование (с точки зрения эффективности) для создания метода конкатенации std :: vector, однако это потребует некоторого сложного совместного использования управления узлами, и, вероятно, поэтому это не было сделано.
  • 3
    @FauChristian: Нет, с точки зрения эффективности не может быть пользы. Векторная память должна быть непрерывной, поэтому то, что вы предлагаете, невозможно. Если вы хотите «некоторого изощренного совместного использования управления узлами» и если вы хотите изменить векторный класс таким образом, вы получите в итоге deque. Даже тогда очень сложно повторно использовать память предложенным способом, хотя она станет немного более выполнимой. Я не думаю, что это в настоящее время реализовано. Главное, что при таком совместном использовании узлов управления (deque) конечный узел может быть частично пустым.
Показать ещё 2 комментария
Теги:
vector
concatenation
stdvector
stl

18 ответов

626
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
  • 49
    Я бы добавил код только для того, чтобы сначала получить количество элементов, содержащихся в каждом векторе, и установить вектор1 как самый большой. Если вы делаете иначе, вы делаете много ненужного копирования.
  • 29
    У меня вопрос. Будет ли это работать, если vector1 и vector2 одинаковые векторы?
Показать ещё 8 комментариев
143

Если вы используете С++ 11 и хотите перемещать элементы, а не просто копировать их, вы можете использовать std::move_iterator вместе со insert (или copy):

#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 в конце.

  • 6
    Метод std :: make_move_iterator () помог мне при попытке объединить std :: vectors из std :: unique_ptr.
126

Я бы использовал функцию insert, например:

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
70

Или вы могли бы использовать:

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

Этот шаблон полезен, если два вектора не содержат точно такого же типа вещей, потому что вы можете использовать что-то вместо std:: back_inserter для преобразования из одного типа в другой.

  • 7
    метод копирования не очень хороший способ. Он будет вызывать push_back несколько раз, что означает, что если нужно вставить много элементов, это может означать многократное перераспределение. лучше использовать вставку, так как реализация вектора могла бы сделать некоторую оптимизацию, чтобы избежать перераспределений. он может зарезервировать память перед началом копирования
  • 6
    @Yogesh: само собой разумеющееся, но ничто не мешает вам позвонить в reserve . Причина, по которой std::copy иногда полезна, заключается в том, что вы хотите использовать что-то, кроме back_inserter .
Показать ещё 3 комментария
39

С С++ 11 я бы предпочел добавить вектор b к a:

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

когда a и b не перекрываются, и b больше не будет использоваться.


Это std::move из <algorithm>, а не обычный std::move из <utility>.

  • 8
    Неопределенное поведение, если a на самом деле - b (что нормально, если вы знаете, что этого никогда не произойдет, но стоит знать об этом в коде общего назначения).
  • 0
    @MartinBonner Спасибо за упоминание этого. Вероятно, я должен вернуться к старому способу insert , который является более безопасным.
Показать ещё 4 комментария
32
std::vector<int> first;
std::vector<int> second;

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

Я предпочитаю тот, который уже упоминался:

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

Но если вы используете С++ 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());
}
  • 2
    std:: выводится через аргумент-зависимый поиск . end(a) будет достаточно.
  • 2
    @Asu ADL будет добавлять только std:: если тип a происходит от std , что исключает общий аспект.
Показать ещё 2 комментария
9

Вы должны использовать вектор :: insert

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

С диапазоном v3 у вас может быть ленивая конкатенация:

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

Демо.

4

Если вы заинтересованы в надежной гарантии исключения (когда конструктор копирования может вызвать исключение):

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 с сильной гарантией не может быть реализован вообще, если конструктор перемещения элемента вектора может выбросить (что маловероятно, но все же).

  • 0
    Разве нельзя v1.erase(... тоже скинуть?
  • 0
    insert уже обрабатывает это. Кроме того, этот вызов erase эквивалентен resize .
3

Здесь универсальное решение, использующее С++ 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.

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));
  • 4
    Хотя этот фрагмент кода может решить проблему, он не объясняет, почему и как он отвечает на вопрос. Пожалуйста, включите объяснение вашего кода , так как это действительно помогает улучшить качество вашего сообщения. Флаггеры / рецензенты: для ответов только с кодом, таких как этот, downvote, не удаляйте! (Примечание: этот ответ на самом деле может быть достаточно простым, чтобы дать объяснение, и, следовательно, отрицательные отзывы не нужны. Вы все еще можете добавить объяснение, чтобы предотвратить появление дополнительных флагов NAA / VLQ.)
2

Общее повышение производительности для конкатенации - проверка размера векторов. И объедините/вставьте меньший с большим.

//vector<int> 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
    Так просто, но я никогда не думал об этом таким образом!
2

Добавьте этот файл в свой файл заголовка:

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]

  • 0
    Не знаю, почему за это проголосовали. Это может быть не самый эффективный способ сделать это, но это не так и эффективно.
1

Вы можете подготовить свой собственный шаблон для оператора +:

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
0

Это решение может быть немного сложным, но у 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), что может быть разумным.

0

Честно говоря, вы могли бы быстро объединить два вектора путем копирования элементов из двух векторов в другой или просто добавить один из двух векторов!. Это зависит от вашей цели.

Метод 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());
  • 3
    Что добавляет ваш ответ, который еще не был представлен в других ответах?
  • 10
    @Mat: жирные символы.
Показать ещё 2 комментария
0

Если то, что вы ищете, это способ добавить вектор к другому после создания, vector::insert - ваш лучший выбор, как было несколько раз ответ, например:

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

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

К сожалению, нет способа построить a const vector<int>, как и выше, вы должны построить, а затем insert.


Если то, что вы действительно ищете, является контейнером для хранения конкатенации этих двух vector<int> s, может быть что-то более доступное для вас, если:

  • Ваш vector содержит примитивы
  • Скрытые примитивы размером 32 бит или меньше
  • Вам нужен контейнер const

Если все это верно, я бы предложил использовать basic_string, который char_type соответствует размеру примитива, содержащего в вашем vector. Вы должны включить static_assert в свой код, чтобы подтвердить эти размеры:

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

С этим удерживанием true вы можете просто сделать:

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

Для получения дополнительной информации о различиях между string и vector вы можете посмотреть здесь: https://stackoverflow.com/questions/4557009/vector-vs-string

Для живого примера этого кода вы можете посмотреть здесь: http://ideone.com/7Iww3I

Ещё вопросы

Сообщество Overcoder
Наверх
Меню