2.

文字列連結演算子完全ガイド — Java/PHP/JS の違い

編集
この記事の要点
  • 言語別の連結演算子: 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 += sO(n²)古い JDK では特に遅い
Java StringBuilder.appendO(n)★ ループでは必須
Java Collectors.joiningO(n)Stream API で簡潔
PHP .=O(n)PHP は文字列が可変
PHP implodeO(n)★ 高速で読みやすい
Python +=O(n²)CPython は文字列が不変
Python "".join(list)O(n)★ Python の流儀
JS str += sO(n)V8 が最適化
JS arr.push + joinO(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 等を併用。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 算術演算子
  2. 文字列演算子
  3. 代入演算子
  4. 比較演算子
  5. 論理演算子
  6. ビット演算子

最近更新/作成されたページ