繁体   English   中英

按索引删除列表元素是否比按值删除列表元素更有效?

[英]Is removing a list element by index more efficient than removing it by value?

在python中,假设我们有以下列表: my_list = ['a', 'b', 'c', 'd', ...]

my_list.pop(3)是否比my_list.remove('d')更有效?

根据此答案 ,这两种方法的复杂度为:

pop     O(n - i)
remove  O(n)

具有列表长度n和元素i索引。

因此,从长远来看,根据列表的大小, pop 可能会更快。

对于小的列表来说没关系:

[wander@box ~]$ python -m timeit '[1, 2, 3].pop(1)'
10000000 loops, best of 3: 0.167 usec per loop
[wander@box ~]$ python -m timeit '[1, 2, 3].remove(2)'
10000000 loops, best of 3: 0.129 usec per loop

如果您的列表确实很大,或者比较列表中的元素需要花费很长时间,则可能会有更多的不同。 删除会比较慢,因为它必须遍历(并比较)所有元素:

[wander@box ~]$ python -m timeit -n 100000 'list(range(1, 100)).pop(98)'
100000 loops, best of 3: 0.916 usec per loop
[wander@box ~]$ python -m timeit -n 100000 'list(range(1, 100)).remove(98)'
100000 loops, best of 3: 2.05 usec per loop

这就是整数,它们的比较速度非常快。 如果列表中的元素具有更有趣的__eq__方法,则remove可能需要很长时间:

class Foo:
    def __eq__(self, other):
        time.sleep(1)
        return False

[Foo(), Foo(), Foo(), Foo(), 20].remove(20)

因此,如果您知道索引,请使用pop

查看listpoplistremove的实际C代码(您打算使用CPython,对吗?),您可以看到:

  1. .remove需要遍历列表(因此,按O(i)进行缩放,其中i是项目的索引);

  2. .pop采用快捷方式,例如:

    • 弄清楚索引是否超出范围(第928行)很简单,但是.remove必须检查整个列表以找到目标(或者不这样做);

    • 特殊情况下,索引是列表中的最后一项(第933行);

  3. listremove调用了PyObject_RichCompareBool (第2200行),这是相对昂贵的,因为它需要检查当前索引处的对象是否等于该项目。

  4. 一旦找到合适的切片位置,两者(除了上面提到的.pop的特殊情况除外)最终都委托给list_ass_slice (941和2202行-后面没有咯咯笑声); 这必须将数组其余部分中的项目一起拖曳,所以O(n - i)也会如此。

在此基础上, .pop会更快,尤其对于列表中的最后一项; 但是,如果您是从项目本身开始的,并且已经进行了O(n)操作并进行了丰富的比较以找到其.index ,那么您在回旋处已经失去的秋千收益。

同样,重新排列数组中剩余的所有内容(即list_ass_slice )以弥补您已删除的内容( .pop.remove需要这样做)的操作可能比找出要删除的项目要昂贵得多。首先删除。

注意,在不深入源代码的情况下,只需从逻辑上考虑每个操作涉及的内容,就可以弄清楚上面的所有内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM