【问题标题】:STL vector of vectors - good idea? Mechanics of of resizing "inner" vector?向量的 STL 向量 - 好主意吗?调整“内部”矢量大小的机制?
【发布时间】:2013-03-13 10:54:27
【问题描述】:

我经常需要表示从整数 0...N-1 到某种类型的列表 T 的映射。对于各个列表,我需要在末尾动态添加元素。 N 通常是事先知道的(但不是在编译时)。我需要快速访问各个列表。

我通常使用vector<vector<T> > my_map(N) 来实现这个,我使用my_map[key].push_back(val) 添加元素。

我有两个问题:

这是实现此类地图的有效且推荐的方法吗?

另外,我想知道元素的连续性及其对调整大小的影响。比如说,我用my_map[key].push_back(val) 添加了一个元素,而my_map[key] 和key != N-1 需要调整大小。这是否会触发整个向量 my_map 的副本以保持其内容连续?还是my_map在内部通过指向堆上的向量的指针实现?

我知道这可能取决于 STL 实现。我主要对 Visual Studio 2010 和 Linux 中的 GCC 的机制(和速度影响)感兴趣。


更新

在 cmets 中,@PeterWood 将我指向 std::deque 作为列表的容器,不需要重新分配即可增长。我做了一些不科学的基准测试来比较vector< deque<T> > 和vector< vector<T> > 和unsigned int 作为T。对于这两种情况,我分别为 100 万个包含 30 个元素的列表和 10,000 个包含 3000 个元素的列表计时。请注意,我的测试反映了这种数据结构的典型应用场景。

我定时了一个随机访问“构建”,其工作原理如下:

vector<ContainerT> my_map(numKeys);
vector<unsigned int> random_keys(numKeys);
for (unsigned int i=0; i<numKeys; ++i) random_keys[i] = i;
random_shuffle(random_keys.begin(),random_keys.end());

for (auto pKey=random_keys.begin(); pKey!=random_keys.end(); ++pKey)
{
  for (unsigned int i=0; i<listSize; ++i)
  {
    my_map[*pKey].push_back( rand() );
  }
}

我定时从随机选择的列表中查询 3000 万个随机元素。

结果

deque 对于许多小列表的构建速度稍快,但在两种情况下,查询速度都比向量慢方式。我的结论是,对于我的问题类型,我会继续使用 vector&lt; vector&lt;T&gt; &gt;。

deque

Keys: 1000000, list size: 30
Mean time buildup: 1.29517 seconds
Mean time query: 4.17624 seconds

Keys: 10000, list size: 3000
Mean time buildup: 0.998761 seconds
Mean time query: 5.052 seconds


vector

Keys: 1000000, list size: 30
Mean time buildup: 1.5347 seconds
Mean time query: 1.63043 seconds

Keys: 10000, list size: 3000
Mean time buildup: 0.604954 seconds
Mean time query: 1.58328 seconds

【问题讨论】:

  • 为什么不使用std::map 的向量?如果您的(整数)键不连续,那么这可能是一个更好的解决方案。
  • @JoachimPileborg 我最初也是这么想的,但是 OP 声明向量的大小在运行时是已知的。这意味着通过索引访问每个向量比通过映射更快。据我了解,在最初填充 my_map 之后,它不需要在运行时调整大小,它不会改变。
  • 您应该指定您使用的是 C++03 还是 C++11。由于移动构造函数,C++11 将具有接近最佳的行为。 C++03 可能会复制很多。
  • @JoachimPileborg:我的整数键是连续的,我真的很喜欢 O(1) 访问我的个人列表。
  • @PeterWood:我添加了一些简单基准测试的结果,得出的结论是我坚持使用vector&lt;vector&lt;T&gt; &gt;。

标签: c++ data-structures vector stl


【解决方案1】:

这是实现此类地图的有效且推荐的方法吗?

我认为这是实现这样一个地图的完全合理的方式。

另外,我想知道元素的连续性及其对调整大小的影响。比如说,我用my_map[key].push_back(val) 添加了一个元素,而my_map[key] 和key != N-1 需要调整大小。这是否会触发整个向量 my_map 的副本以保持其内容连续?还是my_map在内部用指向堆上向量的指针实现?

不,它不会触发整个外部向量的副本。只有子向量是连续的;整个向量通常不是。

就心智模型而言,您可以将my_map 视为一维数组的指针数组,而不是单个连续的二维数组。

【讨论】:

  • 如果内向量(行)中最终包含的对象类是不可移动的,这是否也意味着内向量也是不可移动的,因此外向量将别无选择复制超出容量的调整大小?
  • @WhozCraig:可能是这样。但是,只有分析才能判断这是否具有任何相关性。
【解决方案2】:

它复制外向量中的向量对象。但是这些向量内的堆上的对象不会被重新分配。

【讨论】:

    【解决方案3】:

    每个 std::vector 通常都有一个内部指针,指向它分配 T 类型对象数组的内存。

    在这种类型 T 的向量的向量中,调整内部向量的大小不会调整外部向量的大小(在您的示例中为 my_map),因为外部向量只能被视为指向此类数组的指针向量。内部向量数组位于外部向量之外的其他内存位置。

    出于同样的原因,my_map 的内部向量之间没有邻接

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-09
      • 1970-01-01
      相关资源
      最近更新 更多