【问题标题】:One liner to convert from list<T> to vector<T>一个从 list<T> 转换为 vector<T> 的衬垫
【发布时间】:2011-07-10 06:37:24
【问题描述】:

是否有将list&lt;T&gt; 转换为vector&lt;T&gt; 的单行代码?

谷歌搜索返回给我很多使用手动、冗长转换的结果,这让我呕吐。我们应该费那么大劲来做一些简单的事情,比如列表到向量的转换吗?

【问题讨论】:

  • 像列表到向量转换这样简单的事情对我来说似乎是一个奇怪的说法......虽然你可以作为一个单行来做到这一点(见答案) ,抽象出来的操作并没有那么简单
  • 您是否考虑过对开始和结束迭代器进行操作而不是复制集合?换句话说,你只是想对数据进行操作还是真的有必要进行转换?

标签: c++ stl


【解决方案1】:

您只能使用列表中的所有元素创建一个新向量:

std::vector<T> v{ std::begin(l), std::end(l) };

其中lstd::list&lt;T&gt;。这会将列表中的所有元素复制到向量中。

从 C++11 开始,如果您不再需要原始列表,这可以提高效率。您可以将所有元素移动到向量中,而不是复制:

std::vector<T> v{ std::make_move_iterator(std::begin(l)), 
                  std::make_move_iterator(std::end(l)) };

【讨论】:

  • 迭代器确实是一个强大的概念。 :)
  • 虽然这是 O(N),但效率不是很高。 std::vector&lt;T&gt; v; v.reserve(l.size()); v.append(l.begin(), l.end()); 更接近最佳状态。问题是列表迭代器没有有效的std::distance,它是 O(N),即使 std::list::size 可以是 O(1)(并且将在 C++0x 中)
  • @MSalters:他们如何使std::list::size O(1) 仍然保持 O(1) std::list::splice
  • @MSalters:不幸的是,vector 没有append 功能。应该是v.insert(v.end(),l.begin(),l.end())
  • @MSalters,如果std::vector 对它需要分配的内存量(元素)一无所知,std::vector&lt;T&gt; v(l.begin(), l.end()); 怎么可能是O(n)?它要么分配超出需要的部分,要么需要重新分配每个n 插入,这可能需要n 时间。如果您每次重新分配,这不应该是O(n*n),或者如果您每次通过将元素数量加倍来重新分配向量已满,这不应该是O(n*log(n))吗?
【解决方案2】:

这个怎么样?

list<T> li;
vector<T> vi;        
copy(li.begin(),li.end(),back_inserter(vi));

【讨论】:

  • copy 不知道预先的总元素数。所以back_inserter 只会push_back 每个元素,导致向量溢出几次。反过来,这将导致重新分配 + 复制所有元素。 (或移动,使用来自 C++0x 的右值引用)
  • @xtofl:这是一个合理的担忧。如果您在向量上使用“保留”来预先分配内存怎么样?这将避免我猜的重新分配和复制?如果向量已经存在,则 back_inserter 解决方案可能是一个不错的解决方案。只是指出可能有一些 Space_C0wb0y 解决方案的替代方案,这当然很好。
  • @xtofl:一个问题......构造函数 v(l.begin(),l.end()) 是如何知道总元素数的?它只是得到 2 个指针。
  • @Xeo:你不能为 any 迭代器使用end-begin:虽然它恰好适用于vector::iterator,但它肯定不适用于list::iterator!。至少,使用std::distance
  • @Xeo:在这个问题的上下文中(转换整个容器),它们并不是完全随机的。
【解决方案3】:

接受的答案:

std::vector<T> v(std::begin(l), std::end(l));

当然是正确的,但考虑到最近要求std::list::size()O(1) 的变化,它(很不幸)不是最优的。 如果你有一个符合 std::list 的实现(例如,gcc 直到 5+ 才有),那么下面的速度会快很多(大约 50% 一旦我们达到 50 多个元素):

std::vector<T> v;
v.reserve(l.size());
std::copy(std::begin(l), std::end(l), std::back_inserter(v));

它不是一个单一的衬垫,但你总是可以将它包裹在一个衬垫中。

【讨论】:

  • MSalter 已经在对已接受答案的评论中反对这一点。你能解释一下这增加了什么吗?
  • @black 这是一个答案,而不是评论。而且没有vector::append()
  • 嗯,它是insert,带有一个额外的参数。我不会认为它完全没用/质量低下,因此值得一票否决,但恕我直言,它更接近评论而不是答案。 YMMV。
  • oneliner 很好,但是如果向量应该包含列表的迭代器,则需要注意:std::vector&lt;typename T::iterator&gt; v{ std::begin(l), std::end(l) }; 它可以毫无怨言地编译,但你总是会得到两个元素,最后一个无效。
  • @Erwin411 是的,自从我写了这个答案后,我就开始对列表初始化保持警惕。
【解决方案4】:

虽然这个线程已经很老了,因为“append()”不再可用,我想展示更新的emplace_back one-liner:

v.emplace_back(l.begin(), l.end());

但是这样每个元素都会被重新构造,所以可能不是最快的解决方案!

【讨论】:

    【解决方案5】:

    另一种简单的方法:

    list<int> l {1, 2, 3, 4};
    vector<int> v(l.begin(), l.end());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 2012-02-24
      相关资源
      最近更新 更多