我经常需要将整数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感兴趣。
在评论中,@ PeterWood指出我将std::deque
作为列表的容器,不需要重新分配来增长。我做了一些不科学的基准测试,将vector< deque<T> >
与vector< vector<T> >
与unsigned int
作为T
进行比较。对于两者,我定时了100万个包含30个元素的列表,以及10,000个列表,每个包含3000个元素。请注意,我的测试反映了我对此类数据结构的典型应用场景。
我定时随机访问“build-up”,其工作原理如下:
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< vector<T> >
保持联系。
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
答案 0 :(得分:2)
这是实现这种地图的有效推荐方式吗?
我认为这是实现这种地图的完美合理方式。
另外,我想知道元素的连续性及其对调整大小的影响。说,我添加了一个
my_map[key].push_back(val)
的元素,而my_map[key]
需要key != N-1
来调整大小。这会触发整个向量my_map
的副本以保持其内容连续吗?或者是my_map
在内部实现了指向堆上向量的指针?
不,它不会触发整个外部矢量的副本。只有子矢量是连续的;整个矢量一般不是。
就心理模型而言,您可以将my_map
视为指向1D数组的指针数组,而不是单个连续的2D数组。
答案 1 :(得分:0)
它复制外部矢量中的矢量对象。但是这些向量内的堆上的对象不会被重新分配。
答案 2 :(得分:0)
每个std :: vector通常都有一个指向内存的内部指针,它指向T类型的对象数组。
在这种类型为T的向量向量中,调整内部向量的大小不会调整外部向量的大小(在您的示例中为my_map
),因为外部向量只能被视为指向此类数组的指针向量。内部矢量数组位于除外部矢量之外的其他存储器位置。
出于同样的原因,my_map