由于这是一个性能问题,所以让我们做一些时间安排:
def test_set(xs): seen = set() # O(1) lookups for x in xs: if x not in seen: seen.add(x) else: return ximport collectionsdef test_counter(xs): freq = collections.Counter(xs) for k in freq: if freq[k] > 1: return kdef test_dict(xs): d = {} for x in xs: if x in d: return x d[x] = 1def test_sort(xs): ys = sorted(xs) for n in range(1, len(xs)): if ys[n] == ys[n-1]: return ys[n]##import sys, timeitprint (sys.version + "n")xs = list(range(10000)) + [999]fns = [p for name, p in globals().items() if name.startswith('test')]for fn in fns: assert fn(xs) == 999 print ('%50s %.5f' % (fn, timeit.timeit(lambda: fn(xs), number=100)))我正在测试一个整数列表,而不是一个字符串(因为使用字符串,您最多只能得到256个循环)。我的机器上的结果如下所示:
3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] <function test_set at 0x1020f7380> 0.19265 <function test_dict at 0x1020f7490> 0.12725 <function test_sort at 0x1020f7518> 0.04683 <function test_counter at 0x1020f7408> 0.92485
因此,排序方法似乎是赢家。我猜这是因为它不会浪费时间创建哈希和分配dict /
set结构。另外,如果您不关心源列表的更改,可以使用
xs.sort()代替
ys = sorted(xs),这将为您提供零内存占用。
另一方面,如果重复的项目很可能在输入的开头发生(如
xs = 'abcdef' *10000),则该
set方法将表现最佳,因为它不同于
sort或
Counter,一旦找到重复项就立即返回,不需要预处理整个列表。
set如果需要第
一个 重复元素,而不仅仅是其中一个,则还应该使用。
Counter是一个不错的工具,但它不是为性能而设计的,因此,如果您真的必须处理“大量输入”,请使用集(如果它们适合内存)或合并排序。



