std::list
有一个remove
方法,而std::vector
却没有。这是什么原因?
答案 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+1
到N-1
的所有元素。 <algorithm>
标头中的各种功能实现了该行为,例如std::remove
或std::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)
要从容器中删除元素,您必须先找到它。排序和未排序的向量之间存在很大差异,因此通常不可能为向量实现有效的删除方法。