| この記事の要点 |
- 言語別の連結演算子: Java/JS は
+、PHP は .、Python は + または f-string - Java の
+ はオーバーロード相当で、文字列があれば他方を自動 toString 連結 - ループ内連結は StringBuilder を使う (Java) /
implode (PHP) / join (Python/JS) - JavaScript はテンプレートリテラル
`${var}`、Python は f-string f"{var}" が現代的 - 文字列比較は Java
.equals()、PHP ===、Python ==。Java の == は参照比較
|
言語別の文字列連結演算子
| 言語 | 連結 | テンプレート / 補間 |
| Java | + | String.format / printf / text block (Java 15+) |
| PHP | . ドット | "Hello $name" (ダブルクオート / heredoc) |
| Python | + | f-string f"Hello {name}" (3.6+) |
| JavaScript | + | テンプレートリテラル ` ${name} ` |
| C# | + | 補間文字列 $"Hello {name}" |
| Ruby | + | 式埋め込み "Hello #{name}" |
| Kotlin | + | 文字列テンプレート "Hello $name" |
Java の + 演算子
String name = "Taro";
int age = 25;
String msg = "Name: " + name + ", Age: " + age;
// → "Name: Taro, Age: 25"
// 数値同士 + 文字列の評価順
System.out.println(1 + 2 + "abc"); // "3abc" (左から評価)
System.out.println("abc" + 1 + 2); // "abc12"
// null との連結
String x = null;
System.out.println("val=" + x); // "val=null" ← null と書かれる (例外にならない)
// オブジェクトとの連結 → toString が呼ばれる
List<Integer> list = List.of(1, 2, 3);
System.out.println("list=" + list); // "list=[1, 2, 3]"
StringBuilder (Java: ループ内連結)
// ❌ 遅い: 毎回新しい String を生成 (O(n²))
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i + ","; // ★ 1000 回新規生成
}
// ✅ StringBuilder で O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i).append(",");
}
String result2 = sb.toString();
// ✅ Stream で結合
String result3 = IntStream.range(0, 1000)
.mapToObj(String::valueOf)
.collect(Collectors.joining(","));
// ※ ただし、コンパイル時に決まる連結は最適化される
String fixed = "a" + "b" + "c"; // コンパイル時に "abc" になる
// String dyn = a + b + c; // 単一 + 連は javac が StringBuilder に変換 (Java 8 以前)
// // Java 9+ では invokedynamic で最適化
PHP の . (ドット) 演算子
$name = "Taro";
$age = 25;
// ドット連結
$msg = "Name: " . $name . ", Age: " . $age;
// ダブルクオート内の補間 (連結より読みやすい)
$msg2 = "Name: $name, Age: $age";
$msg3 = "Name: {$name}, Age: {$age}"; // 波括弧でプロパティ呼び出しも可
$msg4 = "Email: {$user->email}";
// シングルクオートでは ★ 補間されない
$msg5 = 'Hello $name'; // → そのまま "Hello $name"
// .= 複合代入
$buf = "";
foreach (range(1, 1000) as $i) {
$buf .= $i . ",";
}
// implode (★ ループ連結の置き換えとして高速)
$buf2 = implode(",", range(1, 1000));
Python の + と f-string
name = "Taro"
age = 25
# + 連結 (型が違うと TypeError)
msg = "Name: " + name + ", Age: " + str(age)
# % フォーマット (古い)
msg = "Name: %s, Age: %d" % (name, age)
# str.format (中庸)
msg = "Name: {}, Age: {}".format(name, age)
msg = "Name: {name}, Age: {age}".format(name=name, age=age)
# ✅ f-string (Python 3.6+, ★ 推奨)
msg = f"Name: {name}, Age: {age}"
msg = f"Total: {price * quantity:.2f}" # 式 + フォーマット指定
msg = f"{name=}, {age=}" # Python 3.8+ デバッグ用
# ループ連結は join
chars = ["a", "b", "c"]
s = ",".join(chars) # "a,b,c"
JavaScript の + とテンプレートリテラル
const name = "Taro";
const age = 25;
// + 連結
const msg = "Name: " + name + ", Age: " + age;
// テンプレートリテラル (バッククォート、★ 推奨)
const msg2 = `Name: ${name}, Age: ${age}`;
const msg3 = `Total: ${(price * quantity).toFixed(2)}`;
const msg4 = `多行も
書ける
普通に`;
// 数値 + 文字列の落とし穴
console.log(1 + 2 + "3"); // "33" (1 + 2 = 3, 3 + "3" = "33")
console.log("1" + 2 + 3); // "123" (左から文字列扱い)
// 配列の join
const list = [1, 2, 3];
console.log(list.join(",")); // "1,2,3"
console.log("" + list); // "1,2,3" (toString 呼ばれる)
// ループ連結は配列 push + join が高速
const buf = [];
for (let i = 0; i < 1000; i++) buf.push(i);
const result = buf.join(",");
Java 15+ Text Block (複数行文字列)
String html = """
<html>
<body>
<p>Hello, %s!</p>
</body>
</html>
""".formatted(name);
String json = """
{
"name": "%s",
"age": %d
}
""".formatted(name, age);
文字列比較 (.equals vs ==)
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // true (文字列プールで共有)
System.out.println(a == c); // false (new で別オブジェクト)
System.out.println(a.equals(c)); // true (内容比較)
// ★ 必ず .equals() を使う
if (str.equals("OK")) { ... }
if ("OK".equals(str)) { ... } // Yoda 記法 (str が null でも NPE 回避)
// 大文字小文字無視
if (str.equalsIgnoreCase("OK")) { ... }
// Java 7+ switch で String 使える (内部で equals 呼び出し)
switch (str) {
case "A" -> ...;
case "B" -> ...;
}
null との連結 (各言語の挙動)
| 言語 | 例 | 結果 |
| Java | "val=" + null | "val=null" (例外なし) |
| PHP | "val=" . null | "val=" (null は空文字扱い) |
| JavaScript | "val=" + null | "val=null" |
| Python | "val=" + None | ★ TypeError |
ループ内連結の落とし穴 (パフォーマンス比較)
| 手法 | 計算量 | 備考 |
Java str += s | O(n²) | 古い JDK では特に遅い |
Java StringBuilder.append | O(n) | ★ ループでは必須 |
Java Collectors.joining | O(n) | Stream API で簡潔 |
PHP .= | O(n) | PHP は文字列が可変 |
PHP implode | O(n) | ★ 高速で読みやすい |
Python += | O(n²) | CPython は文字列が不変 |
Python "".join(list) | O(n) | ★ Python の流儀 |
JS str += s | O(n) | V8 が最適化 |
JS arr.push + join | O(n) | 巨大ループで有効 |
FAQ
Q: PHP で連結が + ではなく . な理由
A: PHP は型が緩いので + を数値加算専用にして混同を防いだ。"1" + "2" === 3 だが "1" . "2" === "12"。
Q: Java で str + null が例外にならないのはなぜ?
A: + 演算子の文字列連結は String.valueOf(obj) 経由で行われ、null は "null" 文字列に変換される仕様。
Q: f-string とテンプレートリテラルでサニタイズは必要?
A: HTML や SQL に直接埋め込むときは要エスケープ。SQL は必ず prepared statement、HTML は htmlspecialchars / DOMPurify 等を併用。