矢量与来自 STL 的列表 - 删除方法
vector vs. list from stl - remove method
std::list
有一个remove
方法,而std::vector
没有。这是什么原因呢?
>std::list<>::remove
是一种物理删除方法,可以通过物理销毁满足某些条件的列表元素来实现(物理销毁是指元素存储持续时间的结束)。物理删除仅适用于列表。它不能应用于数组,如 std::vector<>
.根本不可能以物理方式结束数组中单个元素的存储持续时间。数组只能作为一个整体创建和销毁。这就是为什么std::vector<>
没有类似于std::list<>::remove
的东西。
适用于所有可修改序列的通用删除方法是所谓的逻辑删除:通过用位于序列中更下方的元素值覆盖目标元素的值,从序列中"删除"目标元素。 即,通过向"向左"复制持久数据来移动和压缩序列。这种逻辑删除是通过独立函数实现的,例如 std::remove
。这些功能同样适用于std::vector<>
和std::list<>
。
在基于立即物理删除特定元素的方法适用的情况下,它将比我上面提到的逻辑删除的通用方法更有效。这就是为什么值得专门为std::list<>
提供它的原因.
>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
(和朋友)实际上不会从他们操作的容器中删除物品的类似原因;他们只是将所有不会移除的物品移动到容器的"前面"。 如果不知道如何从容器中实际删除对象,通用算法实际上无法删除。
考虑这两个容器的实现细节。vector
必须提供连续的内存块进行存储。为了删除索引 n != N
处的元素(N
是向量的长度),需要移动从 n+1
到 N-1
的所有元素。<algorithm>
标头中的各种函数实现该行为,如std::remove
或std::remove_if
。这些独立函数的优点是它们可以适用于提供所需迭代器的任何类型。
另一方面,list
是作为链表结构实现的,因此:
- 从任何地方删除元素都很快
- 使用迭代器不可能有效地做到这一点(因为必须知道和操纵内部结构)。
一般来说,在STL中,逻辑是"如果它可以有效地完成 - 那么它就是一个类成员。如果它效率低下 - 那么它是一个外部功能"
通过这种方式,他们区分了类的"正确"(即"有效")使用与"不正确"(低效)的使用。
例如,随机访问迭代器具有 += 运算符,而其他迭代器使用 std::advance
函数。
在这种情况下 - 从std::list
中删除元素非常有效,因为您不需要像在std::vector
中那样移动剩余的值
这一切都与效率和引用/指针/迭代器有效性有关。 可以在不干扰任何其他指针和迭代器的情况下删除list
项。 除了最微不足道的情况外,对于vector
和其他容器来说,情况并非如此。没有什么可以阻止使用外部策略,但您有更好的选择。也就是说,这个家伙在一个重复的问题上说得比我好
来自另一张关于重复问题的海报:
问题不在于为什么 std::vector 不提供操作,而是 而是为什么 std::list 提供它。STL的设计重点突出 关于容器的分离和算法 迭代器,以及在可以实现算法的所有情况下 在迭代器方面有效,这是选项。
但是,在某些情况下,某些特定操作可以 通过了解容器更有效地实现。 这就是从容器中删除元素的情况。成本 使用删除-擦除习惯用法在容器的大小上是线性的 (不能减少太多),但这掩盖了这样一个事实,即在 最坏的情况是,除了其中一个操作之外,所有操作都是对象的副本 (唯一匹配的元素是第一个),这些副本可以 代表相当大的隐性成本。
通过将操作实现为 std::list 中的方法,复杂性 的操作仍将是线性的,但相关成本 删除的每个元素都非常低,几个指针 复制并释放内存中的节点。与此同时, 作为列表的一部分实施可以提供更强大的保证: 指向未擦除元素的指针、引用和迭代器 不会在操作中失效。
在特定的算法中实现的另一个示例 容器是 std::list::sort,它使用较少的合并排序 比 std::sort 高效,但不需要随机访问迭代器。
所以基本上,算法是作为自由函数实现的 迭代器,除非有充分的理由提供特定的 在混凝土容器中实施。
std::list
被设计为像链表一样工作。也就是说,它是为恒定时间插入和删除而设计的(您可能会说是优化的)......但访问相对较慢(因为它通常需要遍历列表)。
std::vector
专为恒定时间访问而设计,就像数组一样。 因此,它针对随机访问进行了优化...但是插入和移除实际上只应该在"尾巴"或"末端"完成,在其他地方它们通常会慢得多。
具有不同用途的不同数据结构...因此不同的操作。
要从容器中删除元素,您必须先找到它。排序向量和未排序向量之间存在很大差异,因此通常无法为向量实现有效的删除方法。
- 从链接列表c++中删除一个项目
- C++如何通过用户输入删除列表元素
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 删除列表中的第n个元素
- 你能检查一下为什么在这个代码中从链接列表中删除项目不起作用吗
- 从类型列表中递归删除重复项会导致编译器堆空间错误 (VS2017)
- 如何在不强制转换每个参数的情况下删除初始值设定项列表中从 int 到 char 的缩小转换?
- 删除列表 c++ 中的最后 3 个元素
- 为什么每当我尝试运行此链接列表删除功能时都会收到分段错误错误?
- 我可以从列表中获取对象并复制它们,但如何删除我复制的对象?
- 动态分配列表 - 创建一个函数,用于删除所有包含偶数值的元素
- C++ 从具有开始位置和结束位置的列表中删除
- 从列表向量中删除无法按预期工作
- 从列表C++中删除对象
- 简单的链接列表删除失败
- C++循环链接列表 - 删除所有节点
- 链接列表删除节点C
- 链接列表删除
- 矢量与来自 STL 的列表 - 删除方法
- 运算符== 和列表::删除()