向量与列表从stl - 删除方法

时间:2013-10-31 20:53:14

标签: c++ list vector stl

std::list有一个remove方法,而std::vector却没有。这是什么原因?

7 个答案:

答案 0 :(得分:4)

std::list<>::remove物理删除方法,可以通过物理破坏满足特定条件的列表元素来实现(通过物理破坏,我的意思是元素的存储持续时间的结束)。物理删除仅适用于列表。它不能应用于数组,例如std::vector<>。它不可能物理地结束阵列的单个元素的存储持续时间。数组只能作为一个整体创建和销毁。这就是std::vector<>std::list<>::remove没有任何相似之处的原因。

适用于所有可修改序列的通用删除方法是人们可能称之为逻辑删除:目标元素通过用位于其中的元素值覆盖其值来从序列中“删除”序列。即通过将“持久数据”复制到“左侧”来移动和压缩序列。这种逻辑删除由独立函数实现,如std::remove。此类函数同等程度适用于std::vector<>std::list<>

如果基于特定元素的即时物理删除的方法适用,它将比我上面提到的逻辑删除的通用方法更有效。这就是值得为std::list<>专门提供它的原因。

答案 1 :(得分:3)

std::list::remove删除列表中与提供的值匹配的所有项。

std::list<int> myList;
// fill it with numbers
myList.remove(10); // physically removes all instances of 10 from the list

它有一个类似的函数std::list::remove_if,它允许你指定一些其他的谓词。

std::list::remove(物理上删除元素)需要是一个成员函数,因为它需要知道内存结构(也就是说,它必须更新每个需要的项目的上一个和下一个指针更新,并删除项目),整个函数在线性时间内完成(列表的单个迭代可以删除所有请求的元素,而不会使任何指向剩余项目的迭代器失效)。

您无法从std::vector中物理删除单个元素。您可以重新分配整个矢量,也可以在删除的项目后移动每个元素并调整size成员。这组操作的“最干净”实施将是

// within some instance of vector
void vector::remove(const T& t)
{
    erase(std::remove(t), end());
}

这需要std::vector依赖<algorithm>(当前不需要的东西)。

由于需要“排序”来删除没有多次分配和需要副本的项目。 (您无需对列表进行排序即可以物理删除元素。)

与其他人所说的相反,它与速度无关。它与需要知道数据如何存储在内存中的算法有关。

作为旁注:这也是std::remove(和朋友)实际上没有从他们操作的容器中删除项目的类似原因;他们只是把所有不会被移除的东西移到容器的“前面”。如果不知道如何从容器中实际删除对象,通用算法实际上无法进行删除。

答案 2 :(得分:2)

考虑两个容器的实现细节。 vector必须为存储提供连续的内存块。要删除索引为n != N的元素(N为向量的长度),需要移动n+1N-1的所有元素。 <algorithm>标头中的各种功能实现了该行为,例如std::removestd::remove_if。这些独立功能的优点是它们可以适用于任何提供所需迭代器的类型。

另一方面,list实现为链表结构,因此:

  • 从任何地方删除元素都很快
  • 使用迭代器来实现它是不可能的(因为必须知道并操纵内部结构)。

答案 3 :(得分:1)

一般来说,在STL中,逻辑是“如果它可以有效地完成 - 那么它就是一个类成员。如果它效率低 - 那么它就是一个外部函数”

通过这种方式,他们区分了“正确”(即“有效”)使用类与“不正确”(低效)使用。

例如,随机访问迭代器有一个+ =运算符,而其他迭代器使用std::advance函数。

在这种情况下 - 从std::list删除元素非常有效,因为您不需要像在std::vector中那样移动剩余的值

答案 4 :(得分:1)

所有这些都与效率和参考/指针/迭代器的有效性有关。可以删除list个项而不会干扰任何其他指针和迭代器。除了最微不足道的情况之外,vector和其他容器都不是这样。没有什么可以阻止使用外部策略,但你有一个更好的选择..那说这个家伙说我在一个重复的问题上比我更好

关于重复问题的另一张海报:

  

问题不是为什么std :: vector不提供操作,而是   而为什么std :: list提供它。 STL的设计重点突出   通过分离容器和算法   迭代器,以及可以实现算法的所有情况   在迭代器方面有效,这是一种选择。

     

但是,有些情况下可以进行特定的操作   通过容器知识更有效地实施。   这是从容器中删除元素的情况。的代价   使用移除擦除习语是容器大小的线性   (这不能减少很多),但这隐藏了这个事实   最坏的情况除了其中一个操作之外的所有操作都是对象的副本   (唯一匹配的元素是第一个),那些副本可以   代表了相当大的隐藏成本。

     

通过将操作实现为std :: list中的方法的复杂性   该操作仍然是线性的,但相关的成本   删除的每个元素都非常低,有几个指针   复制和释放内存中的节点。与此同时,   实施作为清单的一部分可以提供更有力的保证:   指向未被擦除的元素的指针,引用和迭代器   在操作中不会失效。

     

在特定实现的算法的另一个示例   container是std :: list :: sort,它使用较少的mergesort   比std :: sort更高效,但不需要随机访问迭代器。

     

基本上,算法是作为自由函数实现的   除非有充分的理由提供特定的迭代器   在具体容器中实施。

答案 5 :(得分:0)

std::list旨在像链接列表一样工作。也就是说,它被设计(你可能会说是优化的)用于恒定时间的插入和移除......但访问速度相对较慢(因为它通常需要遍历列表)。

std::vector专为持续时间访问而设计,就像数组一样。因此它针对随机访问进行了优化...但插入和删除实际上只应该在“尾部”或“结束”进行,而在其他地方它们通常会慢得多。

具有不同目的的不同数据结构......因此不同的操作。

答案 6 :(得分:0)

要从容器中删除元素,您必须先找到它。排序和未排序的向量之间存在很大差异,因此通常不可能为向量实现有效的删除方法。