はじめに

Javaでプログラムを書く際、複数のデータをまとめて扱う「配列」と、文字データを操作する「文字列(String)」は最も頻繁に使用する機能です。配列を使えば同じ型のデータを効率的に管理でき、Stringクラスの豊富なメソッドを活用すれば文字列処理を簡潔に記述できます。

この記事では、配列の宣言・初期化から多次元配列の使い方、Stringクラスの主要メソッド、そしてJava 15以降で正式導入されたテキストブロックまで、段階的に解説します。

配列の宣言と初期化

配列は、同じ型のデータを連続したメモリ領域に格納するデータ構造です。Javaでは配列を使用する前に、宣言と初期化が必要です。

配列の宣言

配列の宣言には2つの書き方がありますが、Javaでは「型名の後に[]を付ける」スタイルが推奨されています。

1
2
3
4
5
6
// 推奨される書き方
int[] numbers;
String[] names;

// C言語スタイル(非推奨だが動作する)
int scores[];

配列の初期化

配列を使用する前に、newキーワードでメモリを確保するか、初期値を直接指定して初期化します。

1
2
3
4
5
6
7
8
// サイズを指定して初期化(要素はデフォルト値で初期化される)
int[] numbers = new int[5];  // 要素はすべて0で初期化

// 初期値を指定して初期化
int[] scores = {85, 90, 78, 92, 88};

// new キーワードと初期値の両方を使用
String[] fruits = new String[]{"りんご", "バナナ", "オレンジ"};

データ型別のデフォルト値

newキーワードで配列を作成した場合、各要素は型に応じたデフォルト値で初期化されます。

データ型 デフォルト値
int, long, short, byte 0
float, double 0.0
boolean false
char '\u0000'(null文字)
参照型(String、配列など) null

配列の操作(アクセス・ループ・コピー)

配列を効果的に活用するために、要素へのアクセス方法、ループ処理、配列のコピーについて理解しましょう。

要素へのアクセス

配列の各要素にはインデックス(添字)を使ってアクセスします。インデックスは0から始まることに注意してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int[] scores = {85, 90, 78, 92, 88};

// 要素の取得
int firstScore = scores[0];  // 85
int thirdScore = scores[2];  // 78

// 要素の変更
scores[1] = 95;  // 90 → 95 に変更

// 配列の長さを取得
int length = scores.length;  // 5

範囲外アクセスに注意

存在しないインデックスにアクセスするとArrayIndexOutOfBoundsExceptionが発生します。

1
2
int[] numbers = {1, 2, 3};
// int value = numbers[3];  // 例外発生!(有効なインデックスは0〜2)

forループによる配列の走査

配列の全要素を処理する場合は、forループを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int[] scores = {85, 90, 78, 92, 88};

// 通常のforループ
for (int i = 0; i < scores.length; i++) {
    System.out.println("scores[" + i + "] = " + scores[i]);
}

// 拡張for文(for-each)- 読み取り専用の場合に推奨
for (int score : scores) {
    System.out.println("点数: " + score);
}

拡張for文はコードが簡潔になりますが、インデックスを取得できない点と、要素の変更ができない点に注意してください。

配列のコピー

配列を別の変数に代入しても、参照がコピーされるだけで実体は共有されます。独立したコピーを作成するには専用のメソッドを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int[] original = {1, 2, 3, 4, 5};

// 参照のコピー(同じ配列を指す)
int[] reference = original;
reference[0] = 100;  // originalも変更される!

// 実体のコピー方法1: Arrays.copyOf()
int[] copy1 = Arrays.copyOf(original, original.length);

// 実体のコピー方法2: System.arraycopy()
int[] copy2 = new int[original.length];
System.arraycopy(original, 0, copy2, 0, original.length);

// 実体のコピー方法3: clone()
int[] copy3 = original.clone();

Arrays.copyOf()を使用する場合は、ファイルの先頭でimport java.util.Arrays;が必要です。

flowchart LR
    subgraph 参照のコピー
        A[original] --> C[配列の実体]
        B[reference] --> C
    end
    subgraph 実体のコピー
        D[original] --> E[配列の実体1]
        F[copy] --> G[配列の実体2]
    end

多次元配列の使い方

Javaでは配列の配列として多次元配列を表現します。最もよく使われるのは2次元配列です。

2次元配列の宣言と初期化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// サイズを指定して初期化(3行4列の2次元配列)
int[][] matrix = new int[3][4];

// 初期値を指定して初期化
int[][] table = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 各行のサイズが異なるジャグ配列
int[][] jagged = new int[3][];
jagged[0] = new int[2];  // 1行目は2要素
jagged[1] = new int[4];  // 2行目は4要素
jagged[2] = new int[3];  // 3行目は3要素

2次元配列へのアクセス

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int[][] table = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 特定の要素にアクセス(行インデックス, 列インデックス)
int value = table[1][2];  // 6(2行目の3列目)

// 行数を取得
int rows = table.length;  // 3

// 特定の行の列数を取得
int cols = table[0].length;  // 3

2次元配列のループ処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
int[][] table = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// ネストしたforループ
for (int i = 0; i < table.length; i++) {
    for (int j = 0; j < table[i].length; j++) {
        System.out.print(table[i][j] + " ");
    }
    System.out.println();
}

// 拡張for文を使用
for (int[] row : table) {
    for (int cell : row) {
        System.out.print(cell + " ");
    }
    System.out.println();
}

Stringクラスの主要メソッド

JavaのStringクラスは不変(イミュータブル)なオブジェクトで、一度作成された文字列は変更できません。文字列を操作するメソッドは、新しい文字列を返します。

文字列の基本操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
String str = "Hello, Java!";

// 文字列の長さを取得
int length = str.length();  // 12

// 特定位置の文字を取得(インデックスは0から)
char ch = str.charAt(0);  // 'H'

// 空文字列かどうかを判定
boolean isEmpty = str.isEmpty();  // false

// 空白のみかどうかを判定(Java 11以降)
String blank = "   ";
boolean isBlank = blank.isBlank();  // true

文字列の検索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
String str = "Hello, Java! Welcome to Java!";

// 文字列が含まれるか判定
boolean contains = str.contains("Java");  // true

// 文字列の開始・終了を判定
boolean starts = str.startsWith("Hello");  // true
boolean ends = str.endsWith("!");  // true

// 文字列の位置を検索
int first = str.indexOf("Java");  // 7
int last = str.lastIndexOf("Java");  // 24

// 見つからない場合は-1を返す
int notFound = str.indexOf("Python");  // -1

文字列の変換

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
String str = "  Hello, Java!  ";

// 大文字・小文字変換
String upper = str.toUpperCase();  // "  HELLO, JAVA!  "
String lower = str.toLowerCase();  // "  hello, java!  "

// 前後の空白を除去
String trimmed = str.trim();  // "Hello, Java!"

// Unicode空白を含めて除去(Java 11以降)
String stripped = str.strip();  // "Hello, Java!"

// 文字の置換
String replaced = str.replace("Java", "World");  // "  Hello, World!  "

部分文字列の取得

1
2
3
4
5
6
7
String str = "Hello, Java!";

// 開始位置から末尾まで
String sub1 = str.substring(7);  // "Java!"

// 開始位置から終了位置の手前まで
String sub2 = str.substring(0, 5);  // "Hello"

文字列の分割と結合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 文字列の分割
String csv = "apple,banana,orange";
String[] fruits = csv.split(",");  // ["apple", "banana", "orange"]

// 文字列の結合(Java 8以降)
String joined = String.join("-", "2026", "01", "03");  // "2026-01-03"

// 配列から結合
String[] words = {"Java", "is", "fun"};
String sentence = String.join(" ", words);  // "Java is fun"

文字列のフォーマット

1
2
3
4
5
6
7
8
9
String name = "田中";
int age = 25;

// String.format()を使用
String formatted = String.format("名前: %s, 年齢: %d歳", name, age);
// "名前: 田中, 年齢: 25歳"

// formatted()メソッド(Java 15以降)
String result = "名前: %s, 年齢: %d歳".formatted(name, age);

文字列の比較(==とequals)

Javaでの文字列比較は、初心者がつまずきやすいポイントです。==演算子とequals()メソッドの違いを正確に理解することが重要です。

==演算子の動作

==演算子は参照(メモリ上のアドレス)が同じかどうかを比較します。

1
2
3
4
5
6
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

System.out.println(s1 == s2);  // true(同じリテラルを参照)
System.out.println(s1 == s3);  // false(異なるオブジェクト)

文字列リテラル("Hello"など)はStringプールに格納され、同じ内容のリテラルは同じ参照を共有します。しかし、new String()で作成した場合は常に新しいオブジェクトが生成されます。

flowchart TB
    subgraph Stringプール
        L["Hello"]
    end
    subgraph ヒープ
        H["Hello(新規オブジェクト)"]
    end
    s1 --> L
    s2 --> L
    s3 --> H

equals()メソッドの動作

equals()メソッドは文字列の内容が同じかどうかを比較します。

1
2
3
4
5
6
7
String s1 = "Hello";
String s2 = new String("Hello");

System.out.println(s1.equals(s2));  // true(内容が同じ)

// 大文字小文字を無視して比較
System.out.println("hello".equalsIgnoreCase("HELLO"));  // true

文字列比較のベストプラクティス

文字列の内容を比較する場合は、常にequals()を使用することを推奨します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
String input = getUserInput();  // ユーザー入力を取得

// 良い例:equals()を使用
if ("yes".equals(input)) {
    System.out.println("承認されました");
}

// リテラルを左側に置くとNullPointerExceptionを防げる
// inputがnullでも例外が発生しない
if ("yes".equals(input)) { /* ... */ }

// 悪い例:==を使用(意図しない結果になる可能性)
// if (input == "yes") { /* ... */ }

StringBuilderで効率的な文字列操作

Stringは不変オブジェクトのため、文字列を繰り返し連結すると毎回新しいオブジェクトが生成され、パフォーマンスが低下します。StringBuilderを使用すると、可変な文字列を効率的に操作できます。

文字列連結のパフォーマンス問題

1
2
3
4
5
// 非効率な例:ループ内での文字列連結
String result = "";
for (int i = 0; i < 10000; i++) {
    result += i;  // 毎回新しいStringオブジェクトが生成される
}

上記のコードでは、ループのたびに新しいStringオブジェクトが生成され、古いオブジェクトはガベージコレクションの対象となります。

StringBuilderの基本操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// StringBuilderの作成
StringBuilder sb = new StringBuilder();

// 文字列の追加
sb.append("Hello");
sb.append(", ");
sb.append("Java!");

// メソッドチェーンも可能
sb.append(" ").append("Version: ").append(21);

// Stringに変換
String result = sb.toString();  // "Hello, Java! Version: 21"

StringBuilderの主要メソッド

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
StringBuilder sb = new StringBuilder("Hello, Java!");

// 指定位置に挿入
sb.insert(7, "World and ");  // "Hello, World and Java!"

// 指定範囲を削除
sb.delete(7, 17);  // "Hello, Java!"

// 指定範囲を置換
sb.replace(7, 11, "World");  // "Hello, World!"

// 文字列を逆順にする
sb.reverse();  // "!dlroW ,olleH"

// 現在の長さを取得
int length = sb.length();

// 容量を取得(内部バッファのサイズ)
int capacity = sb.capacity();

効率的な文字列連結の実装例

1
2
3
4
5
6
7
8
9
// 効率的な例:StringBuilderを使用
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i);
}
String result = sb.toString();

// 初期容量を指定することでさらに効率化
StringBuilder sbWithCapacity = new StringBuilder(50000);  // 予想サイズを指定

StringBufferとの違い

StringBufferStringBuilderとほぼ同じAPIを持ちますが、スレッドセーフ(同期化されている)という違いがあります。

クラス スレッドセーフ パフォーマンス
StringBuilder No 高速
StringBuffer Yes やや遅い

シングルスレッド環境ではStringBuilderを使用し、マルチスレッド環境で複数のスレッドから同時にアクセスする場合のみStringBufferを検討してください。

テキストブロックで複数行文字列を扱う

Java 15で正式導入されたテキストブロックを使用すると、複数行の文字列をより読みやすく記述できます。

従来の複数行文字列

1
2
3
4
5
6
// 従来の書き方:エスケープシーケンスと連結が必要
String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, World!</p>\n" +
              "    </body>\n" +
              "</html>";

テキストブロックの書き方

テキストブロックは"""(ダブルクォート3つ)で囲んで記述します。

1
2
3
4
5
6
7
8
// テキストブロックを使用した書き方
String html = """
              <html>
                  <body>
                      <p>Hello, World!</p>
                  </body>
              </html>
              """;

テキストブロックの特徴

テキストブロックには以下の特徴があります。

インデントの自動調整

コンパイラは「付随的な空白(incidental whitespace)」を自動的に除去します。閉じ"""の位置が基準となります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
String text = """
              行1
              行2
              """;
// 結果: "行1\n行2\n"(先頭のスペースは除去される)

String textWithIndent = """
    行1
        行2(インデント付き)
    """;
// 結果: "行1\n    行2(インデント付き)\n"

エスケープシーケンスの簡略化

1
2
3
4
5
6
7
// 従来:ダブルクォートのエスケープが必要
String json = "{\"name\": \"John\", \"age\": 30}";

// テキストブロック:エスケープ不要
String jsonBlock = """
                   {"name": "John", "age": 30}
                   """;

改行の制御

1
2
3
4
5
6
7
8
9
// 末尾に改行を含めない場合は閉じ"""を同じ行に
String noNewline = """
                   Hello, World!""";

// 行末の\で改行を抑制(Java 14以降)
String singleLine = """
                    Hello, \
                    World!""";
// 結果: "Hello, World!"

テキストブロックの活用例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SQL文
String sql = """
             SELECT id, name, email
             FROM users
             WHERE status = 'active'
             ORDER BY name
             """;

// JSON
String json = """
              {
                  "users": [
                      {"id": 1, "name": "田中"},
                      {"id": 2, "name": "佐藤"}
                  ]
              }
              """;

// 変数の埋め込み(formatted()と組み合わせ)
String name = "山田";
int age = 30;
String template = """
                  名前: %s
                  年齢: %d歳
                  """.formatted(name, age);

まとめ

この記事では、Javaの配列とStringについて以下の内容を解説しました。

  • 配列の宣言と初期化: 型名の後に[]を付ける宣言方法と、newキーワードまたは初期値リストによる初期化
  • 配列の操作: インデックスによるアクセス、forループと拡張for文による走査、Arrays.copyOf()によるコピー
  • 多次元配列: 2次元配列の作成方法とネストしたループによる処理
  • Stringの主要メソッド: length(), charAt(), substring(), split(), join(), replace()などの活用
  • 文字列比較: ==は参照比較、equals()は内容比較という違いの理解
  • StringBuilder: 効率的な文字列連結のための可変文字列クラス
  • テキストブロック: Java 15以降で使える複数行文字列の簡潔な記法

配列と文字列はJavaプログラミングの基盤となる機能です。これらをマスターすることで、より複雑なデータ構造やアルゴリズムの実装に進むことができます。

参考リンク