在 C++ 中访问列表列表的元素
Posted
技术标签:
【中文标题】在 C++ 中访问列表列表的元素【英文标题】:Accessing elements of a list of lists in C++ 【发布时间】:2012-08-30 02:57:12 【问题描述】:我有一个这样的列表:
std::list<std::list<double> > list;
我在其中填充了一些带有双精度值的列表(实际上很多,这就是我不使用向量的原因。所有这些复制都会占用大量时间。)
假设我想访问可以像list[3][3]
这样访问的元素,如果列表不是列表而是向量或二维数组。我该怎么做?
我知道访问列表中的元素是通过使用迭代器来完成的。不过我不知道如何摆脱双打。
【问题讨论】:
你应该使用vector
。仅在需要拼接时使用list
。
@avakar 如果您不需要随机访问并且需要从容器的开始/中间删除元素 - 使用矢量是非常糟糕的主意。
使用vector::reserve
保留内存并避免额外的副本,如果这是您的担忧。此外,即使你可以得到你想要的operator[]
,它也将是非常低效的。
std::vector
在需要重新分配时通常非常聪明,因此它越大,重新分配和复制的次数就越少。 std::list
只有在程序的生命周期内多次创建容器多次时才会更快。
@ForEveR,你是对的,如果你需要从开始/中间插入/删除,你也不应该使用向量。仅仅因为您不需要随机访问并不意味着您不应该使用矢量。
【参考方案1】:
double item = *std::next(std::begin(*std::next(std::begin(list), 3)), 3);
不过,使用矢量通常会有更好的性能;访问列表的元素 n
是 O(n)。
如果你关心容器内部拼接的性能,你可以使用deque
,它有operator[]
,从两端分摊常量插入和删除,从内部线性时间插入和删除。
对于C++03编译器,你可以自己实现begin
和next
:
template<typename Container>
typename Container::iterator begin(Container &container)
return container.begin();
template<typename Container>
typename Container::const_iterator begin(const Container &container)
return container.begin();
template<typename T, int n>
T *begin(T (&array)[n])
return &array[0];
template<typename Iterator>
Iterator next(Iterator it, typename std::iterator_traits<Iterator>::difference_type n = 1)
std::advance(it, n);
return it;
【讨论】:
访问时会这样做,但插入时不会。每次扩展向量的大小时,都必须将向量复制到新的内存位置。由于列表稍后将(不是出于测试目的)以线性方式访问,查看每个元素,我认为它是一个列表并具有一个元素的线性查找时间并不重要 @oaktree:除了你可以reserve() 来缓解这个问题,它总是分配很多额外的空间,以至于它不必在每次调整大小时重新分配。 @PlasmaHH 如果我确切知道需要添加多少元素,我可以保留内存。我为每个列表添加了大约 512 个元素,并且有大约 400 个列表。我知道这还不是一个巨大的数额,但相当多...... @ecatmur 我的 stl 似乎没有std::next()
或包含迭代器根本不够。似乎也缺少std::begin()
@oaktree std::next
和 std::begin
在 C++11 中添加。【参考方案2】:
要真正回答您的问题,您可能应该查看std::advance
。
【讨论】:
【参考方案3】:要严格回答您的问题,Joachim Pileborg's 答案是要走的路:
std::list<std::list<double> >::iterator it = list.begin();
std::advance(it, 3);
std::list<double>::iterator it2 = (*it).begin();
std::advance(it2, 3);
double d = *it2;
现在,从您的问题和进一步的 cmets 来看,尚不清楚您是始终将元素添加到列表的末尾还是可以将它们添加到任何地方。如果你总是添加到最后,vector<double>
会更好。 vector<T>
不需要在每次大小增加时都复制;只有当它的容量增加时,这是一个非常不同的事情。
除此之外,正如其他人之前所说,使用reserve()
将对重新分配有很大帮助。您不需要为所有向量的组合大小保留,而只需为每个单独的向量保留。所以:
std::vector<std::vector<double> > v;
v.reserve(512); // If you are inserting 400 vectors, with a little extra just in case
您还可以为v
中的每个vector<double>
保留。就是这样。
考虑到您的列表将占用更多空间。对于内部列表中的每个double
,它必须分配至少两个额外的指针,并且还必须为全局最少内的每个列表分配两个额外的指针。这意味着你的容器占用的总内存大约是向量的三倍。而且所有这些分配和管理也需要额外的运行时间。
【讨论】:
advance
不能用于诸如begin
返回的临时文件。您需要使用next
。
我忘了添加一些东西。如果您真的不知道容器的最终大小并且担心分配太多或太少的空间,请考虑使用deque<>
。它通常比vector<>
慢一点,但增长得更好,因为它不需要复制自己。为此,deque<>
没有方法reserve()
。
@ecatmur:事实上,advance()
不像我写的那样工作。我正在编辑代码。以上是关于在 C++ 中访问列表列表的元素的主要内容,如果未能解决你的问题,请参考以下文章
在 DART 中:如何访问 String 列表的元素 1,它是 Map 主列表元素 1 的键“respostas”的值?