Python集与列表


187

在Python中,哪种数据结构更有效/更快速?假设顺序对我而言并不重要,并且无论如何我都将检查重复项,那么Python设置是否比Python列表慢?

Answers:


230

这取决于您打算如何处理。

在确定对象是否存在于集合中时,集合要快得多(如中所示x in s),但是在遍历其内容时要比列表慢。

您可以使用timeit模块查看哪种情况适合您的情况。


4
对于您的观点:“集合显着更快”,什么使它更快的底层实现?
外汇兑换

脚本语言喜欢隐藏底层的实现,但是这种明显的简化并不总是一件好事,在设计软件时,您确实需要一定的“数据结构”意识。
Christophe Roussy

4
Set在迭代时不会比list慢很多。
omerfarukdogan

39
集和列表都具有线性时间迭代。说一个人比另一个人“慢”是被误导的,并且使阅读此答案的新程序员感到困惑。
哈比人

@habnabit,如果您说它们都具有线性时间迭代。这是否意味着它们具有相同的迭代时间?那有什么区别呢?
Mohammed Noureldin

152

当您只想遍历值时,列表比集合要快一些。

但是,如果要检查项目中是否包含项目,则集合的速度明显快于列表。它们只能包含唯一项。

事实证明,除了不变性之外,元组的执行几乎与列表完全相同。

反复进行

>>> def iter_test(iterable):
...     for i in iterable:
...         pass
...
>>> from timeit import timeit
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = set(range(10000))",
...     number=100000)
12.666952133178711
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = list(range(10000))",
...     number=100000)
9.917098999023438
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = tuple(range(10000))",
...     number=100000)
9.865639209747314

确定是否存在对象

>>> def in_test(iterable):
...     for i in range(1000):
...         if i in iterable:
...             pass
...
>>> from timeit import timeit
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = set(range(1000))",
...     number=10000)
0.5591847896575928
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = list(range(1000))",
...     number=10000)
50.18339991569519
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = tuple(range(1000))",
...     number=10000)
51.597304821014404

6
我发现(Initializing set-> 5.5300979614257812)(Initializing list-> 1.8846848011016846)(Initializing tuple-> 1.8730108737945557)我的具有12GB RAM的Intel Core i5四核的大小为10,000的项目。这也应予以考虑。
ThePracticalOne 2014年

4
我已经更新了代码,现在可以删除对象的创建。timeit循环的设置阶段仅被调用一次(docs.python.org/2/library/timeit.html#timeit.Timer.timeit)。
Ellis Percival 2014年

7

列表效果:

>>> import timeit
>>> timeit.timeit(stmt='10**6 in a', setup='a = range(10**6)', number=100000)
0.008128150348026608

设置效果:

>>> timeit.timeit(stmt='10**6 in a', setup='a = set(range(10**6))', number=100000)
0.005674857488571661

您可能要考虑元组,因为它们与列表相似,但是无法修改。它们占用的内存略少,并且访问速度更快。它们不像列表那样灵活,但效率更高。它们的正常用途是用作字典键。

集也是序列结构,但与列表和元组有两个区别。尽管集合确实具有顺序,但是该顺序是任意的,不在程序员的控制之下。第二个区别是集合中的元素必须唯一。

set根据定义。[ python | Wiki ]。

>>> x = set([1, 1, 2, 2, 3, 3])
>>> x
{1, 2, 3}

4
首先,您应该更新到set内置类型链接(docs.python.org/2/library/stdtypes.html#set),而不是不推荐使用的sets库。其次,“集也是序列结构”,请从内置类型链接中阅读以下内容:“集是无序集合,不记录元素位置或插入顺序。因此,集不支持索引,切片或其他类似序列的行为。”
Seaux 2014年

7
range不是listrange是带有自定义__contains__魔术方法的特殊类。
Ryne Wang '18

@RyneWang这是正确的,但仅适用于Python3。在Python2中,范围返回一个普通列表(这就是为什么存在可怕的东西之类的原因xrange
Manoel Vilela 18/12/11

7

Set由于近乎即时的“包含”检查而获胜:https//en.wikipedia.org/wiki/Hash_table

列表实现:通常是一个数组,靠近金属层较低,适合于迭代和按元素索引随机访问。

设置实现:https : //en.wikipedia.org/wiki/Hash_table,它不会在列表上进行迭代,而是通过计算键中的哈希值来找到元素,因此它取决于键元素和哈希值的性质功能。类似于用于字典的内容。我怀疑list如果元素很少(<5)可能会更快,元素计数越大,set包含检查的性能越好。它也可以快速添加和删除元素。还请始终牢记,构建一套需要付出代价!

注意:如果list已经对进行了排序,则搜索list可能会很快,但是对于通常情况set,包含检查的a 会更快,更简单。


8
靠近金属?在Python上下文中,这甚至意味着什么?清单比金属更接近金属吗?
roganjosh

@ roganjosh,python仍在机器上运行,某些实现(如“数组”列表)更接近于硬件擅长的领域:stackoverflow.com/questions/176011/…,但它始终取决于您要实现的目标,它很高兴了解一些实现,而不仅仅是抽象。
克里斯托弗·罗西

2

tl; dr

数据结构(DS)很重要,因为它们用于对数据执行操作,这基本上意味着:接受一些输入,对其进行处理,然后返回输出

在某些特定情况下,某些数据结构比其他数据结构更有用。因此,询问哪个(DS)更有效/更快是相当不公平的。这就像问刀和叉之间哪种工具更有效。我的意思是所有情况都取决于情况。

清单

列表是可变序列通常用于存储同类项目的集合

套装

集合对象是不同的可哈希对象无序集合。它通常用于测试成员资格,从序列中删除重复项以及计算数学运算(例如交集,并集,差和对称差)。

用法

从一些答案中可以明显看出,迭代值时列表比集合快得多。另一方面,检查项目是否包含列表时,集合比列表快。因此,对于某些特定操作,您唯一能说的是列表比集合要好,反之亦然。


2

当使用CPython检查值是否为少量文字之一时,我对结果感兴趣。set在Python 3 vs中获胜tuplelist并且or

from timeit import timeit

def in_test1():
  for i in range(1000):
    if i in (314, 628):
      pass

def in_test2():
  for i in range(1000):
    if i in [314, 628]:
      pass

def in_test3():
  for i in range(1000):
    if i in {314, 628}:
      pass

def in_test4():
  for i in range(1000):
    if i == 314 or i == 628:
      pass

print("tuple")
print(timeit("in_test1()", setup="from __main__ import in_test1", number=100000))
print("list")
print(timeit("in_test2()", setup="from __main__ import in_test2", number=100000))
print("set")
print(timeit("in_test3()", setup="from __main__ import in_test3", number=100000))
print("or")
print(timeit("in_test4()", setup="from __main__ import in_test4", number=100000))

输出:

tuple
4.735646052286029
list
4.7308746771886945
set
3.5755991376936436
or
4.687681658193469

对于3到5个字面量,set仍然会以较大幅度获胜,并or成为最慢的。

在Python 2中,set总是最慢的。or是最快的2至3文本和tuplelist是具有4个或多个文字更快。我无法区分tuplevs 的速度list

当要测试的值被缓存在函数之外的全局变量中,而不是在循环中创建文字set时,即使在Python 2中,每次也会赢。

这些结果适用于Core i7上的64位CPython。


0

我建议您使用用例仅限于引用或搜索存在的Set实现,以及使用用例需要您执行迭代的Tuple实现。列表是低级别的实现,需要大量的内存开销。


1
确实,在何时使用Set和何时使用Tuple之间进行适当的区分确实至关重要。除非我编写较低级别的API脚本,否则我不会担心所涉及的内存开销和占用空间。

0
from datetime import datetime
listA = range(10000000)
setA = set(listA)
tupA = tuple(listA)
#Source Code

def calc(data, type):
start = datetime.now()
if data in type:
print ""
end = datetime.now()
print end-start

calc(9999, listA)
calc(9999, tupA)
calc(9999, setA)

比较所有3的10次迭代后的输出: 比较


0

集合更快,而且您可以通过集合获得更多功能,比如说您有两个集合:

set1 = {"Harry Potter", "James Bond", "Iron Man"}
set2 = {"Captain America", "Black Widow", "Hulk", "Harry Potter", "James Bond"}

我们可以轻松地加入两个集合:

set3 = set1.union(set2)

找出两者的共同点:

set3 = set1.intersection(set2)

找出两者的不同之处:

set3 = set1.difference(set2)

以及更多!只是尝试一下,它们很有趣!此外,如果您必须处理2个列表中的不同值或2个列表中的公用值,我更喜欢将列表转换为集合,许多程序员都采用这种方式。希望它对您有帮助:-)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.