连接两个std :: vector

621 c++ stl vector concatenation stdvector

我如何连接两个std::vector

Rob*_*ble 668

vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
Run Code Online (Sandbox Code Playgroud)

  • 我只添加代码以首先获取每个向量所包含的元素数,并将vector1设置为保持最大的元素.如果你不这样做,你会做很多不必要的复制. (52认同)
  • 我有个问题.如果vector1和vector2是相同的向量,这会工作吗? (31认同)
  • @AlexanderRafferty:仅当`vector1.capacity()> = 2*vector1.size()`时.除非你调用`std :: vector :: reserve()`,否则这是非典型的.否则向量将重新分配,使作为参数2和3传递的迭代器无效. (30认同)
  • 太糟糕了,标准库中没有更简洁的表达方式.`.concat`或`+ =`等等 (23认同)
  • 如果你将几个向量连接到一个,那么首先在目标向量上调用`reserve`是否有帮助? (5认同)
  • @FaheemMitha:由于`insert`的参数是向量,它已经知道前面有多少个元素并且将自己处理它.如果我们插入其他类似数组的东西,那么首先保留空间是有用的. (5认同)
  • @nmr在C++中,这是_quite_ succinct. (3认同)
  • 很难概述这个问题的所有不同答案。`std::vector::insert` 方法也已发布在其他几个答案中,但是这个答案得到了最多的支持,所以我会在这里询问/声明:使用 `insert` 应该比其他使用的方法有优势`back_inserter` 因为 `insert` 可能会批量进行 memcpy,而使用 `back_inserter` 的方法必须迭代要逐个元素插入的向量。我这样看对吗? (2认同)

小智 169

如果您正在使用C++ 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;
}
Run Code Online (Sandbox Code Playgroud)

对于带有整数的示例,这不会更有效,因为移动它们并不比复制它们更有效,但对于具有优化移动的数据结构,它可以避免复制不必要的状态:

#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;
}
Run Code Online (Sandbox Code Playgroud)

移动后,src的元素保留在未定义但安全的构造状态,并且其前面的元素最后直接转移到dest的新元素.

  • std::make_move_iterator() 方法在尝试连接 std::unique_ptr 的 std::vectors 时帮助了我。 (6认同)
  • 这和`std::move(src.begin(), src.end(), back_inserter(dest))`有什么区别? (4认同)
  • @kshenoy,“插入”可能会在一轮中分配必要的内存量。当“back_inserter”可能导致多次重新分配时 (4认同)
  • @road_to_quantdom 这将使用 move-constructor 将元素从 src 移动到 dest。如果没有 std::make_move_iterator 它将使用复制构造函数。 (3认同)

Tom*_*ter 132

我会使用insert函数,如:

vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Run Code Online (Sandbox Code Playgroud)


Rog*_*mbe 74

或者您可以使用:

std::copy(source.begin(), source.end(), std::back_inserter(destination));
Run Code Online (Sandbox Code Playgroud)

如果两个向量不包含完全相同类型的东西,则此模式很有用,因为您可以使用某些东西而不是std :: back_inserter将一种类型转换为另一种类型.

  • 复制方法不是一个好方法.它将多次调用push_back,这意味着如果必须插入许多元素,这可能意味着多次重新分配.最好使用insert作为向量实现可以做一些优化以避免重新分配.它可以在开始复制之前保留内存 (7认同)
  • @Yogesh:被授予,但没有什么能阻止你先调用`reserve`.`std :: copy`有时候有用的原因是你想要使用`back_inserter`以外的东西. (6认同)
  • 即使有保留,副本也很糟糕。vector::insert 将避免所有检查:http://quick-bench.com/bLJO4OfkAzMcWia7Pa80ynwmAIA (2认同)
  • @SamuelLi - 如果出现问题,则主要是push_back中的“if&gt;capacity_”。这是一个足够大的问题,以至于“resize”中的“memset”并不重要。 (2认同)

Deq*_*ing 53

使用C++ 11,我更喜欢将vector附加到a:

std::move(b.begin(), b.end(), std::back_inserter(a));
Run Code Online (Sandbox Code Playgroud)

何时ab不重叠,b不再使用.


这是std::move来自<algorithm>,而不是通常 std::move<utility>.

  • 啊,OTHER std :: move.第一次看到它时相当混乱. (13认同)
  • 如果实际上是b,那么未定义的行为(如果您知道这种情况永远不会发生,那就没关系 - 但值得在通用代码中注意). (9认同)
  • 这与带有 `move_iterator` 的 `insert()` 不同吗?如果是这样,如何? (3认同)
  • 我已经添加了一个关于我们在这里谈论的 `std::move` 的注释,因为大多数人不知道这个重载。希望这是一个改进。 (2认同)

Jam*_*ran 33

std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());
Run Code Online (Sandbox Code Playgroud)


ST3*_*ST3 22

我更喜欢已经提到的一个:

a.insert(a.end(), b.begin(), b.end());
Run Code Online (Sandbox Code Playgroud)

但是如果你使用C++ 11,还有一种更通用的方法:

a.insert(std::end(a), std::begin(b), std::end(b));
Run Code Online (Sandbox Code Playgroud)

此外,不是问题的一部分,但建议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());
}
Run Code Online (Sandbox Code Playgroud)

  • `std ::`是通过[参数依赖查找]推导出来的(http://en.cppreference.com/w/cpp/language/adl).`end(a)`就足够了. (2认同)
  • @Asu ADL只会添加`std ::`,如果`a`的类型来自`std`,这会破坏通用方面. (2认同)
  • 您最好不要使用保留,因为它可能会带来巨大的开销。看这里:/sf/answers/4487163481/ (2认同)

Vik*_*Roy 18

连接的一般性能提升是检查向量的大小。并将较小的与较大的合并/插入。

//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());
}
Run Code Online (Sandbox Code Playgroud)

  • 示例代码不正确。`v1.insert(v2.end()...` 使用迭代器进入 `v2` 来指定 `v1` 中的位置。 (2认同)
  • 我不喜欢这个答案,因为在所有情况下都不会在 v1 之后插入 v2 (没有用注释指定它)。否则,如果您添加一个将串联保存在另一个向量中而不是修改其中一个向量的解决方案,您的答案可能会更完整。 (2认同)

Dan*_*ger 13

如果您希望能够简洁地连接向量,您可以重载+=运算符。

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;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样称呼它:

vector1 += vector2;
Run Code Online (Sandbox Code Playgroud)


小智 12

你应该使用vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());
Run Code Online (Sandbox Code Playgroud)

  • 这不是和Tom Ritter和Robert Gamble在2008年给出的答案一样吗? (6认同)

Jar*_*d42 11

使用范围v3,您可能会有一个懒惰的连接:

ranges::view::concat(v1, v2)
Run Code Online (Sandbox Code Playgroud)

演示.

  • 我预计这将在 2023 年左右成为合适的答案。 (4认同)
  • @ReinstateMonica3167040 真糟糕——我也在等待飞行汽车。也许是 2024 年。 (4认同)

Olp*_*pah 10

使用 C++20,您可以摆脱带有范围的 begin() 和 end()。

#include <ranges>

std::ranges::copy(vec2, std::back_inserter(vec1));
Run Code Online (Sandbox Code Playgroud)

或者如果你想移动元素:

std::ranges::move(vec2, std::back_inserter(vec1));
Run Code Online (Sandbox Code Playgroud)


Ale*_*exT 9

如果您对强异常保证感兴趣(当复制构造函数可以抛出异常时):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

append_move如果向量元素的移动构造函数可以抛出(这不太可能,但仍然存在),则与强保证类似,一般无法实现。


Pav*_*aka 9

有一个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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为它并不比“std::vector::insert”更容易使用,但它的用途有所不同:将两个范围合并到一个新范围中,而不是将一个向量插入到另一个向量的末尾。答案中值得一提吗? (8认同)

LoS*_*LoS 9

在 C++23 中,可以使用其成员函数append_range(如果存在)将范围连接到标准容器。

因此,两个对象的串联std::vector可以通过以下方式执行:

vec1.append_range(vec2);
Run Code Online (Sandbox Code Playgroud)


Ste*_*nko 6

将此添加到您的头文件中:

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;
}
Run Code Online (Sandbox Code Playgroud)

并以这种方式使用它:

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);
Run Code Online (Sandbox Code Playgroud)

r 将包含 [1,2,62]

  • @leeo 非 const ref args 之一 (2认同)

Ron*_*sse 6

如果您的目标只是为了只读目的而遍历值的范围,另一种方法是将两个向量都包裹在代理 (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
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,包括“VecProxy”实现以及优缺点,请参阅/sf/answers/3908713091/