Java | 基礎文法:文字列型

Java Java
スポンサーリンク

文字列型の全体像

Java の文字列は String クラスで表現され、テキストを扱うための最重要型です。String は不変(immutable)で、一度作った文字列の内容は変わりません。この不変性により、スレッド安全性や予測可能性が向上しますが、繰り返しの編集や結合では別のオブジェクトが都度生成されます。効率性を保つため、編集が多い処理では StringBuilderStringBuffer を使うのが基本です。


基本操作と代表的メソッド

作成、長さ、取り出し

文字列はリテラル "..." や既存データから生成します。長さは length()、個々の文字は charAt(index) で取得します。

String s = "Java";
int len = s.length();      // 4
char first = s.charAt(0);  // 'J'
Java

部分文字列は substring(beginIndex, endIndex) を使います。インデックスは 0 始まりで、endIndex は「含まない」ことに注意します。

String sub = s.substring(1, 3); // "av"
Java

検索には indexOflastIndexOf を使います。見つからない場合は -1 が返ります。

int pos = "hello".indexOf("ll"); // 2
Java

等価比較と大小変換(重要ポイントの深掘り)

equals と == の違い

== は参照の同一性(同じオブジェクトか)を比較し、equals は文字列内容の同等性を比較します。文字列の比較は必ず equals を使います。

String a = new String("hi");
String b = "hi";
System.out.println(a == b);       // false(別オブジェクト)
System.out.println(a.equals(b));  // true(内容が同じ)
Java

大文字・小文字を無視する比較は equalsIgnoreCase を使います。言語特有の大文字・小文字変換は複雑なので、単純なケース以外ではロケールを考慮します。

大文字・小文字とロケール

toUpperCase()toLowerCase() はロケール依存の挙動を持つ場合があります。英語前提なら toUpperCase(Locale.ROOT) のようにルートロケールを指定すると安定します。

String title = "i18n";
String upper = title.toUpperCase(Locale.ROOT); // "I18N"
Java

連結と性能、文字列の不変性(重要ポイントの深掘り)

連結の基本とコンパイラ最適化

小規模の連結は + で十分です。コンパイラは単一式内の複数連結を StringBuilder に最適化します。ただしループ内での都度連結はオブジェクトを大量生成し、性能劣化や GC 負荷の原因になります。

String msg = "Hello, " + "Java " + 21; // 単一式は最適化される
Java

ループ内連結は StringBuilder

繰り返しや大量結合では StringBuilder を使います。初期容量を見積もって指定すると再割り当てが減り、さらに効率化できます。

StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 100; i++) {
    sb.append("item").append(i).append('\n');
}
String result = sb.toString();
Java

String の不変性は安全性の源泉ですが、編集が必要な場面では「編集用(Builder)→ 最終文字列化」のパターンを徹底すると保守性と性能が両立します。


分割、結合、整形表示

分割とトリム

split は正規表現で分割します。単純な区切り文字なら正規表現のメタ文字に注意し、必要ならエスケープします。前後の空白は trim で除去します。

String line = "a,b,c";
String[] parts = line.split(","); // ["a", "b", "c"]
String cleaned = "  hello  ".trim(); // "hello"
Java

結合とフォーマット

配列やコレクションの結合は String.join が簡潔です。整形表示には String.formatSystem.out.printf を使い、桁数や小数点の制御ができます。

String joined = String.join(" / ", List.of("A", "B", "C")); // "A / B / C"
String msg = String.format("total=%,d price=%.2f", 12345, 1999.5); // "total=12,345 price=1999.50"
Java

正規表現と置換、検証

置換と正規表現

単純置換は replace、正規表現を使う置換は replaceAll を選びます。正規表現では .* などが特別な意味を持つため、パターン設計に注意します。

String masked = "user-1234".replaceAll("\\d", "*"); // "user-****"
Java

複雑な検証やグルーピングが必要なら PatternMatcher を使うと効率的です(コンパイル済みパターンの再利用)。

Pattern p = Pattern.compile("([A-Za-z]+)-(\\d+)");
Matcher m = p.matcher("id-42");
if (m.matches()) {
    String word = m.group(1); // "id"
    String num = m.group(2);  // "42"
}
Java

Unicode、コードポイント、テキストブロック(重要ポイントの深掘り)

Unicode と code point

String は内部的に UTF-16 を使います。BMP 以外の文字(絵文字など)はサロゲートペアで表現され、charAt 1 文字では崩れることがあります。「文字数」を正確に扱うにはコードポイント API を使用します。

String emoji = "😊";
int codePoints = emoji.codePointCount(0, emoji.length()); // 1
int cp = emoji.codePointAt(0);
Java

コードポイントを意識することで、絵文字混在テキストのカーソル移動や切り詰め表示で文字化けを防げます。

エスケープとテキストブロック

改行は \n、タブは \t、引用符は \"(ダブル)や \'(シングル)を使ってエスケープします。複数行の文字列は Java 15+ のテキストブロックが便利です。

String json = """
{
  "name": "Java",
  "ver": 21
}
""";
Java

テキストブロックは可読性が高く、テンプレートや埋め込みリソースの記述に向いています。


文字列とバイト列の相互変換、null 安全性

エンコーディングの指定

バイト列への変換は文字コードが必須です。プラットフォーム依存を避けるため StandardCharsets を常に明示します。

byte[] bytes = "こんにちは".getBytes(StandardCharsets.UTF_8);
String restored = new String(bytes, StandardCharsets.UTF_8);
Java

文字コードを誤ると「文字化け」します。外部との境界(ファイル、ネットワーク、DB)では UTF-8 を基本に統一すると安全です。

null 安全性と NPE 回避

String 変数が null の可能性がある場合、メソッド呼び出しは NullPointerException の原因になります。入力境界で Objects.requireNonNull を使う、あるいは「空文字(””)で正規化」する方針を明確にしましょう。

String input = Objects.requireNonNull(arg, "arg is required");
Java

例題で総復習

例 1: ログ行のパースと整形

public class LogParser {
    public static void main(String[] args) {
        String line = "INFO,2025-12-17,User login,id=42";
        String[] tokens = line.split(",");
        String level = tokens[0];
        String date = tokens[1];
        String msg = String.join(" | ", tokens[2], tokens[3]);
        System.out.printf("[%s] %s %s%n", level, date, msg);
    }
}
Java

分割→抽出→結合→整形表示の流れをコンパクトに表しています。入力が不完全な場合に備え、実務では配列長のチェックや検証を追加します。

例 2: ループ連結の最適化

public class JoinDemo {
    public static void main(String[] args) {
        String[] words = {"Java", "String", "Builder"};
        StringBuilder sb = new StringBuilder();
        for (String w : words) {
            if (sb.length() > 0) sb.append(", ");
            sb.append(w);
        }
        System.out.println(sb.toString()); // "Java, String, Builder"
    }
}
Java

StringBuilder を使い、ループ内の連結コストを抑えています。区切り文字の扱いも sb.length() でシンプルに制御しています。

例 3: 正規表現でマスキング

public class MaskDemo {
    public static void main(String[] args) {
        String card = "card=1234-5678-9012-3456";
        String masked = card.replaceAll("\\d{4}(?=\\b)", "****");
        System.out.println(masked); // "card=****-****-****-****"
    }
}
Java

先読み((?=...))を使い、数字ブロックごとに安全に置き換えています。正規表現は「必要最小限のパターン」で設計すると読みやすく保てます。


設計の指針(重要ポイントのまとめ)

文字列は「不変」を前提に設計し、編集は StringBuilder に委ねるのが定石です。比較は常に equals、ロケールが絡む変換は Locale.ROOT を指定して予期せぬ変換を避けます。分割と置換では正規表現のメタ文字に注意し、外部境界では文字コード(UTF-8)を明示して文字化けを防止します。Unicode とコードポイントの理解は、絵文字や多言語を扱う現場でのトラブル回避に直結します。

タイトルとURLをコピーしました