Pythonでの文字列連結スピード比較（2025年版）

## 概要

Pythonで複数の文字列を連結する方法として、次の3つの方法の処理速度を比較します。

1 配列に格納して「.join()」で連結
```py
l = []
l.append("a")
l.append("b")
l.append("c")
t = "".join(l)

print(t)
# abc
```
2 「StringIO」でwrite()を使う
```py
from io import StringIO
b = StringIO()
b.write("a")
b.write("b")
b.write("c")
t = b.getvalue()

print(t)
# abc
```
3 文字列変数を「+」で連結
```py
t = ""
t += "a"
t += "b"
t += "c"

print(t)
# abc
```

## 背景

大量の文字列を扱う際など、配列とjoin()を使うことが多かったのですが、サンプルコードなどではStringIOを使用しているものも見かけるため、調べてみました。

## 参考事例

2012年と古い記事ですが比較をしている記事がありました。

[Lists vs. StringIO vs. Regular String Concat for building strings in Python](https://gist.github.com/djfroofy/3258823)

結果部分を引用します。
連結速度は
「+」<「.join()」<「StringIO」
となっています。
(注意：2025年では結果が違います)

```
IPython 0.10.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [2]: from string_building_perf import *

In [3]: %timeit build_ul_list(100000)
10 loops, best of 3: 77.9 ms per loop

In [4]: %timeit build_ul_stringio(100000)
10 loops, best of 3: 109 ms per loop

In [5]: %timeit build_ul_concat(100000)
10 loops, best of 3: 66.1 ms per loop

In [6]: %timeit build_ul_list(100000, get_random_word)
1 loops, best of 3: 245 ms per loop

In [7]: %timeit build_ul_stringio(100000, get_random_word)
1 loops, best of 3: 269 ms per loop

In [8]: %timeit build_ul_concat(100000, get_random_word)
1 loops, best of 3: 203 ms per loop
```

## 比較(2025)

同じ方法で改めて計測してみます(Python3.12.3, ipython9.2.0)。
コードの一部をPython3に合わせて修正して使わせてもらっています。

```
# パターン1

%timeit build_ul_list(100000)
6.34 ms ± 95.4 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit build_ul_stringio(100000)
5.76 ms ± 86.4 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit build_ul_concat(100000)
6.25 ms ± 109 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# パターン2

%timeit build_ul_list(100000, get_random_word)
19.6 ms ± 195 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit build_ul_stringio(100000, get_random_word)
18.7 ms ± 197 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit build_ul_concat(100000, get_random_word)
18.6 ms ± 130 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
```

「.join()」が若干遅いくらいで他は差が少ない結果です。
パターン1では「stringIO」が少し早いです。

## 結果
2025年現在では、どの連結方法を使っても速度面での差は少ないと言えそうです。

## 利用コード

2025年の比較では次のコードを使用しています。
オリジナルはリンクを参照してください。

```py
from io import StringIO
from itertools import permutations
import string
import random

word = 'x' * 8
words = [''.join(p) for p in permutations(string.ascii_letters [:8])]

def get_8x():
    return word

def get_random_word():
    return random.choice(words)

def build_ul_list(linect, get_word=get_8x):
    buffer = ['<ul>\n']
    for i in range(linect):
        buffer.append('<li>%s</li>\n' % get_word())
    buffer.append('</ul>')
    return ''.join(buffer)


def build_ul_stringio(linect, get_word=get_8x):
    buffer = StringIO()
    buffer.write('<ul>\n')
    for i in range(linect):
        buffer.write('<li>%s</li>\n' % get_word())
    buffer.write('</ul>')
    return buffer.getvalue()


def build_ul_concat(linect, get_word=get_8x):
    buffer = '<ul>\n'
    for i in range(linect):
        buffer += ('<li>%s</li>\n' % get_word())
    buffer += '</ul>'
    return buffer
```