什么 STL 容器执行删除其间的元素?
Posted
技术标签:
【中文标题】什么 STL 容器执行删除其间的元素?【英文标题】:What STL container to perform removal of elements in between? 【发布时间】:2011-07-12 23:47:04 【问题描述】:我需要选择一个容器来保存指向我定义的类型 (Particle
) 的指针。我正在使用预分配的粒子Object Pool
(其中包含在 std::vector 上预分配的对象)。
我的粒子发射器在需要发射时向粒子池询问粒子(以避免游戏中的粒子分配)。当粒子过期时,它会返回到粒子对象池中。
如您所见,当我遍历我的粒子参考容器(需要选择一个)以更新它时,我必须检查哪些粒子已过期 (lifetime <= 0.0
) 并将它们返回到粒子池, 过期粒子可以在容器中的任何位置。
我一直在考虑使用std::list
,原因如下:
列表 (AFAIK) 在开始时提供恒定时间插入,并在任何点提供恒定时间删除(假设您已经迭代到该点)。
欢迎对我的系统提出任何建议或改进,以便更好地适应您的容器建议。
编辑:
为了更好地解释我自己:
发射器中粒子的寿命不完全相同,它取决于一个范围,例如 5.0 秒 +-(0.0 到 0.5)。这是为了给粒子一个随机元素,在固定时间内看起来比所有的都好。
算法伪代码:
// Assume typedef std::container_type<Particle *> ParticleContainer
void update(float delta)
ParticleContainer::iterator particle = m_particles.begin();
for(; particle != m_particles.end(); ++particle)
updateParticle(*particle, delta); //Update the particle
if ( (*particle)->lifeTime <= 0.0 )
ParticlePool.markAsFree(*particle); //Mark Particle as free in the object Pool
m_particles.remove(*particle); //Remove the Particle from my own ParticleContainer
【问题讨论】:
对于顺序容器,始终以std::vector
开头。然后配置文件,如果容器操作有问题,请尝试另一个容器。 Usually you will find yourself sticking with std::vector
.
“常数时间”和std::list的问题在于常数很大!使用 std::vector 时间是可变的,但很小。你的选择! :-)
【参考方案1】:
我不完全遵循您的算法,但需要std::vector
来提供摊销恒定时间push_back
。迭代时它还可能具有更好的参考局部性。
如果顺序无关紧要,删除任何项目也是一个常数时间操作:
template <typename T>
void remove(std::vector<T> &v, size_t i)
std::swap(v[i], v.back());
v.pop_back();
【讨论】:
我认为正确的术语是amortized constant time,但是,是的,从std::vector
开始,只有在使用另一个容器时分析显示改进后才会更改。 However, I doubt you will find this often.
使用std::swap(v[i], v.back()); v.pop_back();
会更好,因为swap
是不抛出的、恒定的时间和便宜的(对于swap
的所有非白痴实现)。
你好,我编辑了我的帖子。在一个元素上调整矢量大小的成本是多少?当然这比删除中间的元素要好,但是,如果我经常这样做,你认为性能会受到太大影响吗? (不过我得介绍一下)
@Mr.Gando,添加单个元素需要摊销的常数时间。在典型的实现中,重新分配会使向量的容量翻倍,因此每次元素数量翻倍时才需要它。
太棒了!我一直在寻找一种快速、高效(恒定时间)的方法来通过随机访问删除元素。谢谢!【参考方案2】:
为什么不使用priority queue?这样您就不必遍历所有活动粒子。
编辑:再想一想:我不太确定这是否真的有效,这取决于您的算法(我承认我并不完全理解)。如果您要更改该生命周期价值当条目在容器中时,队列可能根本不起作用。
但是,如果您不更改该值(例如,您在插入粒子时设置粒子过期的时间,然后只检查第一个条目与当前时间的对比)我仍然认为这是您的最佳选择。 (请注意,优先级队列只是一个适配器,默认在内部使用std::vector
。)
edit2:关于您的编辑。我建议不要为每个粒子存储一个“生命周期”值(它会不断减少,直到它不再是正数),而是存储一个时间戳,表示应该何时移除粒子。初始化粒子时,只需计算粒子何时到期(通过将您的随机“生命周期”添加到当前时间)。这个值不会在你的粒子的生命周期内改变。在判断是否移除粒子时,只需检查当前时间是否大于过期时间戳即可。
【讨论】:
我编辑了我的帖子,生命周期值没有改变,但对于所有粒子来说并不相同。粒子寿命确实取决于随机“范围”值,例如:5.0 秒 +- 范围(0.0, 0.5)(其中范围给出给定参数之间的随机数) 是的,但您似乎可以在插入时确定“到期日期”,只需将您的(随机)生命周期添加到当前时间,然后存储结果。 (在粒子从容器中取出之前,初始化后过期日期不会改变)。优先级队列将确保内部的所有粒子都是弱排序的,因此您只需弹出条目,直到未来的最高“到期日期”。【参考方案3】:假设您不需要直接索引 (operator[]
) 并且您通常只是在容器上进行迭代,list
听起来不错。您甚至可以使用splice
在恒定时间内移动列表节点,而无需分配或释放内存。
【讨论】:
您好,我编辑了我的帖子,如果您认为您的回复仍然适用,那就太好了:)。【参考方案4】:听起来像std::list
是要走的路。这是假设您肯定要在从中间删除对象的同时遍历列表。请注意,当您从中间移除时,大多数其他容器将使迭代器无效; std::list
没有。其他建议:
slist
(单链表;gcc 支持这一点)——它将节省空间,在删除元素时为您节省一些(少量)运行时成本,因为您'正在减少必须更新的指针数量。这与具有双重链接的std::list
进行比较。
【讨论】:
【参考方案5】:我没有完全理解,但是;
一组粒子指针也可以证明是值得的任务。插入日志(n) 删除日志(n)
1234563删除【讨论】:
以上是关于什么 STL 容器执行删除其间的元素?的主要内容,如果未能解决你的问题,请参考以下文章