为什么join比正常连接更快

时间:2010-02-24 09:44:24

标签: javascript python performance join string-concatenation

我已经看到了来自不同语言的几个例子,这些例子毫不含糊地证明了连接列表(数组)的元素比连接字符串要快一些。不幸的是我没有找到解释原因? 有人可以解释在两种操作下都能运行的内部算法,为什么这种算法比另一种更快。

这是我的意思的一个python例子:

# This is slow
x = 'a'
x += 'b'
...
x += 'z'

# This is fast
x = ['a', 'b', ... 'z']
x = ''.join(x)

感谢提前)

7 个答案:

答案 0 :(得分:13)

原因是Python(以及许多其他语言)中的字符串是immutable objects - 也就是说,一旦创建,它们就无法更改。相反,连接一个字符串实际上会产生一个 new 字符串,该字符串由连接的两个较小字符串的内容组成,然后用新字符串替换旧字符串。

由于创建字符串需要一定的时间(需要分配内存,将字符串的内容复制到该内存等等),因此制作许多字符串所需的时间比制作单个字符串要长。执行 N 级联需要在此过程中创建 N 个新字符串。另一方面,join()只需创建一个字符串(最终结果),因此工作得更快。

答案 1 :(得分:13)

join函数中的代码首先知道要求连接的所有字符串以及这些字符串的大小,因此它可以在开始操作之前计算最终的字符串长度。因此,它只需要为最终字符串分配一次内存,然后它就可以将每个源字符串(和分隔符)放在内存中的正确位置。

另一方面,字符串上的单个+ =操作别无选择,只能为最终字符串分配足够的内存,而最终字符串只是两个字符串的串联。后续+ =必须相同,每个分配内存,在下一个+ =将被丢弃。每次将不断增长的字符串从内存中的一个位置复制到另一个位置。

答案 2 :(得分:3)

这是因为必须为字符串连接分配越来越大的内存块:

x = 'a' # String of size 1 allocated
x += 'b' # String of size 2 allocated, x copied, and 'b' added. Old x discarded
x += 'b' # String of size 3 allocated, x copied, and 'c' added. Old x discarded
x += 'b' # String of size 4 allocated, x copied, and 'd' added. Old x discarded
x += 'b' # String of size 5 allocated, x copied, and 'e' added. Old x discarded

所以会发生什么,你执行大量的分配和副本,但然后转身扔掉它们。非常浪费。

x = ['a', 'b', ..., 'z'] # 26 small allocations
x = ''.join(x) # A single, large allocation

答案 3 :(得分:3)

请参阅python string join performance以及一个非常好描述它的特定anwser:

  

建议是关于连接很多字符串。

     

计算s = s1 + s2 + ... + sn,

     

1)使用+。创建新的字符串s1 + s2,然后创建新的字符串s1 + s2 + s3,...等,因此涉及大量的内存分配和复制操作。实际上,s1被复制n-1次,s2被复制n-2次,......等等。

     

2)使用“”.join([s1,s2,...,sn])。连接在一次传递中完成,字符串中的每个字符只复制一次。

答案 4 :(得分:1)

其他回复基本上已经涵盖了它,但如果你想要更多的细节,Joel Spolsky有一篇文章,他描述了“Schlemiel the painter's algorithm”,这是非常相关的,很好地说明为什么要理解这种即使您使用的是Python等高级语言,低级实现细节仍然非常重要。

答案 5 :(得分:0)

嗯,这很大程度上依赖于语言,但总的来说,有一个大的操作比许多小操作快。在第二个示例中,连接知道它必须连接的所有元素,因此可以只分配必要的资源并放入字符。第一个示例中的连接必须在每一步重新分配资源(最坏的情况)。 / p>

答案 6 :(得分:0)

我不知道join的内部,但是在第一个版本中,每次调用+ =运算符时都会创建一个新字符串。因为字符串是不可变的,所以每次分配新内存并进行复制时。

现在,连接(这是一个字符串方法)只能进行一次分配,因为它可以预先计算大小。