什么 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 没有。其他建议:

如果你(很多)关心空间,你可以试试Boost.Intrusive 如果您愿意使用非标准容器,请尝试slist(单链表;gcc 支持这一点)——它将节省空间,在删除元素时为您节省一些(少量)运行时成本,因为您'正在减少必须更新的指针数量。这与具有双重链接的std::list 进行比较。

【讨论】:

【参考方案5】:

我没有完全理解,但是;

一组粒子指针也可以证明是值得的任务。插入日志(n) 删除日志(n)

1234563删除

【讨论】:

以上是关于什么 STL 容器执行删除其间的元素?的主要内容,如果未能解决你的问题,请参考以下文章

仅通过迭代器删除 STL 容器元素

合并多个 STL 容器、删除重复元素的最佳方法?

STL容器总结

STL—— 容器(vector)元素的删除

在迭代时从地图(或任何其他STL容器)中删除/删除内容

STL容器及适配器