【问题标题】:Why is 'join' faster than normal concatenation?为什么连接比普通连接快
【发布时间】:2011-01-20 11:08:39
【问题描述】:

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

这是我的意思的python示例:

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

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

提前感谢)

【问题讨论】:

  • 当你阅读str.join的代码时,你学到了什么?
  • 对不起,我不明白这个问题。
  • 这里是来源:svn.python.org/view/python/trunk/Objects/…。看了join的源码,你了解到join的速度怎么样?

标签: javascript python performance join string-concatenation


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    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】:

    我不知道 join 的内部原理,但在第一个版本中,每次调用 += 运算符时都会创建一个新字符串。由于字符串是不可变的,因此每次分配新内存并制作副本时。

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          请参阅 python string join performance 和一个很好地描述它的特定答案:

          建议是关于连接大量字符串。

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

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

          2) 使用 "".join([s1,s2,...,sn])。连接是一次性完成的,字符串中的每个字符只复制一次。

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 2014-02-20
              • 2011-11-10
              • 2016-01-19
              • 1970-01-01
              • 2014-03-26
              • 1970-01-01
              • 2016-08-08
              • 2012-01-03
              • 2017-06-17
              相关资源
              最近更新 更多