Javaの配列を自由自在に操る!実務で役立つArraysクラス徹底攻略ガイド

こんにちは。ゆうせいです。

新人エンジニアの皆さん、Javaの配列操作で「もっと楽に書けないかな?」と感じたことはありませんか?

標準で用意されている便利な道具箱、java.util.Arraysクラスを使いこなして、スマートなコードを書けるようになりましょう!

本記事の構成

本講座では、実務で必須となるArraysクラスの活用術を以下のステップで学習します。

  1. 配列の中身を覗き見よう!表示と比較の極意(toString / equals)
  2. バラバラのデータを整列させる!並び替えと探索のルール(sort / binarySearch)
  3. 配列を劇的に作り変える!コピーと穴埋めのテクニック(copyOf / fill)
  4. 柔軟性を手に入れる!リスト変換とストリーム連携(asList / stream)
  5. 配列の限界を知る!固定長設計と可変長引数の正体

第1章 配列の中身を覗き見よう!表示と比較の極意

概要

この章では、配列の内容を文字列として出力する方法と、2つの配列が同じかどうかを正しく判定する方法を学びます。

デバッグ作業やテストコードを書く際に、なくてはならない基本スキルです。

対象メソッド

  • Arrays.toString
  • Arrays.deepToString
  • Arrays.equals
  • Arrays.deepEquals

サンプルコード

import java.util.Arrays;

public class ArrayMaster {
    public static void main(String[] args) {
        String[] fruits1 = {"Apple", "Banana"};
        String[] fruits2 = {"Apple", "Banana"};
        
        System.out.println("1: " + fruits1.toString());
        System.out.println("2: " + Arrays.toString(fruits1));
        System.out.println("3: " + fruits1.equals(fruits2));
        System.out.println("4: " + Arrays.equals(fruits1, fruits2));

        String[][] matrix1 = {{"A", "B"}, {"C", "D"}};
        String[][] matrix2 = {{"A", "B"}, {"C", "D"}};

        System.out.println("5: " + Arrays.deepToString(matrix1));
        System.out.println("6: " + Arrays.deepEquals(matrix1, matrix2));
    }
}

実行結果

1: [Ljava.lang.String;@(ハッシュ値)

2: [Apple, Banana]

3: false

4: true

5: [[A, B], [C, D]]

6: true

逐次解説

  1. fruits1.toString()を実行しても、メモリ上の住所のような情報が出るだけで中身は見えません。
  2. Arrays.toStringを使うことで、配列の要素をカンマ区切りで綺麗に表示できます。
  3. 配列変数に対して直接equalsを使うと、中身ではなく「同じインスタンスか」を比較してしまいます。
  4. Arrays.equalsを使うと、要素が順番通りに一致しているかをチェックしてくれます。
  5. 多次元配列(配列の中に配列がある構造)の場合、通常のtoStringでは内側の配列の中身まで見えません。そこでdeepToStringの出番です。
  6. 多次元配列の比較も同様に、深い階層まで潜って比較してくれるdeepEqualsを使用します。

イメージ

通常のtoStringは、お弁当箱の外側に貼られたラベル(管理番号)だけを見ている状態です。

対してArrays.toStringは、お弁当箱のフタを開けて、中に入っているおかずを一つずつ確認して報告してくれるイメージだと理解してください。

さらに、お弁当箱の中に小さなお弁当箱が入っているような二重構造(多次元配列)のとき、その中の小さなフタまで全部開けてくれるのが「deep」と名の付くメソッドたちです。

よくある誤解

「配列同士を == や .equals() で比較すれば中身の判定ができる」と思い込んでしまうケースが非常に多いです。

Javaにおいて配列はオブジェクトなので、これらは参照先(メモリ上の位置)を比較しているに過ぎません。

中身が全く同じでも、別々に生成された配列であれば false になるという事実に注意してください!

実務での重要性

システム開発において、処理の結果が期待通りになっているかを確認するユニットテストは欠かせません。

「計算結果の配列」と「正解の配列」を比較するとき、Arrays.equalsを知らなければ、わざわざfor文を書いて1つずつ要素を比較する羽目になります。

これを知っているだけで、コードの行数は激減し、読みやすさは格段に向上します。

演習問題

問1:int型の2次元配列 data = {{10, 20}, {30, 40}} の中身をコンソールに一括表示したい場合、どのメソッドを使うのが適切でしょうか?

問2:以下のコードを実行したとき、結果はどうなるでしょうか?理由も併せて考えてください。

int[] a = {1, 2, 3};

int[] b = {1, 2, 3};

System.out.println(Arrays.equals(a, b));


いかがでしたか?まずは基本中の基本である表示と比較を押さえました。

次は、バラバラなデータを一瞬で整列させる方法についてお話ししましょう。


第2章 バラバラのデータを整列させる!並び替えと探索のルール

概要

大量のデータを扱う際、ただ並んでいるだけでは活用しにくいものです。 この章では、データを昇順(小さい順)に並べ替える「ソート」と、特定のデータがどこにあるかを探す「バイナリサーチ」を学びます。 特に、探し物をする時の「ルール」に注目してください。

対象メソッド

  • Arrays.sort
  • Arrays.binarySearch

サンプルコード

import java.util.Arrays;

public class ArraySearchMaster {
    public static void main(String[] args) {
        int[] numbers = {50, 10, 30, 20, 40};

        System.out.println("ソート前: " + Arrays.toString(numbers));
        Arrays.sort(numbers);
        System.out.println("ソート後: " + Arrays.toString(numbers));

        int target = 30;
        int index = Arrays.binarySearch(numbers, target);
        System.out.println(target + "のインデックス番号: " + index);

        int notFound = 99;
        int result = Arrays.binarySearch(numbers, notFound);
        System.out.println(notFound + "を探した結果: " + result);
    }
}

実行結果

ソート前: [50, 10, 30, 20, 40] ソート後: [10, 20, 30, 40, 50] 30のインデックス番号: 2 99を探した結果: -6

逐次解説

  1. 初期状態の配列はバラバラな順序で格納されています。
  2. Arrays.sortを実行すると、配列自体の中身が書き換えられ、昇順(小さい順)に並びます。
  3. 30が何番目にあるかを探すため、binarySearchを使います。0から数えて2番目にあるので「2」が返ります。
  4. 存在しない値(99)を探すと、負の数が返されます。これは「見つからなかった」というサインです。

イメージ

バイナリサーチ(二分探索)は、辞書で単語を引くときの動きに似ています。 いきなり1ページ目からめくるのではなく、まずは真ん中を開きますよね? もし探したい単語がもっと後ろなら、前の半分はもう見なくて済みます。 これを繰り返して範囲を半分、また半分と絞り込んでいくのがバイナリサーチです! 最初から順番に探すよりも圧倒的に速いのですが、この技を使うには「データが順番通りに並んでいる(ソート済み)」という絶対条件が必要になります。

よくある誤解

「Arrays.sortを実行しなくても、binarySearchは動く」と思ってしまうのが最大の罠です。 バラバラな状態の配列に対してbinarySearchを使うと、たとえ中にその値が存在していても、デタラメな結果を返したり、見つからないと判定されたりします。 「探す前に、必ず並べる!」という合言葉を忘れないでくださいね。

実務での重要性

ECサイトの商品一覧を安い順に並べたり、名簿から特定の名前を検索したりする処理は、どんなシステムにも登場します。 Arrays.sortは、内部で非常に効率的なアルゴリズムを使用しているため、自分で複雑な並び替えプログラムを書く必要はありません。 標準機能を信じて使いこなすことが、バグの少ない、高速なシステムを作る近道です。

演習問題

問1:Arrays.binarySearchを使用して正しい結果を得るために、直前に実行しておくべき処理は何でしょうか?

問2:Arrays.sortで並び替えた後の配列に、同じ値が複数含まれていた場合、binarySearchはどのインデックスを返すでしょうか?(ヒント:仕様を確認してみましょう)


データの整理整頓ができるようになると、プログラムがぐっと知的になりますね。 次は、配列のサイズを変えたいときや、特定の値を一気に詰め込みたいときに役立つテクニックを紹介します。


第3章 配列を劇的に作り変える!コピーと穴埋めのテクニック

概要

Javaの配列は、一度作ると後から箱の数を増やすことができない「固定長」という性質を持っています。 「あ、もう一つデータを入れたいのに箱が足りない!」という絶望的な状況を救うのが、新しい大きな箱を作って中身を移し替えるコピーの技術です。 また、初期値で埋め尽くす便利な方法も併せて攻略しましょう。

対象メソッド

  • Arrays.fill
  • Arrays.copyOf
  • Arrays.copyOfRange

サンプルコード

import java.util.Arrays;

public class ArrayModifyMaster {
    public static void main(String[] args) {
        int[] data = new int[5];
        Arrays.fill(data, 7);
        System.out.println("1: " + Arrays.toString(data));

        int[] original = {10, 20, 30};
        int[] expanded = Arrays.copyOf(original, 5);
        System.out.println("2: " + Arrays.toString(expanded));

        int[] part = Arrays.copyOfRange(original, 1, 3);
        System.out.println("3: " + Arrays.toString(part));
    }
}

実行結果

1: [7, 7, 7, 7, 7]

2: [10, 20, 30, 0, 0]

3: [20, 30]

逐次解説

  1. new int[5]で作った直後はすべて0ですが、Arrays.fillを使うと、指定した値(今回は7)で全ての箱を塗りつぶせます。
  2. originalという3つの箱の中身を、5つの新しい箱(expanded)にコピーしています。入り切らなかった後ろの2つには、初期値の0が入ります。
  3. copyOfRangeを使うと、配列の「ここからここまで」と範囲を指定して抜き出せます。1番目から3番目の直前(つまりインデックス1と2)をコピーしています。

イメージ

Arrays.fillは、ペンキの入ったバケツで配列という長い塀を一気に塗りつぶす作業に似ています。 一方でArrays.copyOfは、今あるお弁当箱が小さくなったので、もっと大きなお弁当箱を買ってきて、中身をそのままスライドさせて移し替えるイメージです。 「配列のサイズは変えられないけれど、中身を移した新しい配列を作ることで、サイズ変更を擬似的に実現している」というカラクリを理解してくださいね。

よくある誤解

copyOfRangeを使うとき、終わりの番号の指定に注意が必要です。 例えば (original, 1, 3) と書いた場合、3番目の要素は含まれません。「開始位置以上、終了位置未満」というルールはJavaの多くのメソッドで共通しているので、今のうちに体に染み込ませてしまいましょう! 「3番目の手前まで」と覚えるのがコツですよ。

実務での重要性

初期設定で全てのフラグを「未完了(false)」にしたい時、1つずつ代入するのは面倒ですよね。そんな時にfillが輝きます。 また、通信データやファイル読み込みで、必要な部分だけを切り取って処理したい場合にはcopyOfRangeが必須となります。 「配列は不自由だ」と嘆く前に、これらのツールで賢く立ち回りましょう。

演習問題

問1:要素数が3の配列 {1, 2, 3} を、Arrays.copyOf を使って要素数2の配列にコピーすると、結果はどうなるでしょうか?

問2:Arrays.fill を使って、配列の「特定の範囲だけ」を特定の値で埋めることは可能でしょうか?(APIリファレンスを調べてみましょう!)


配列を自在に加工できるようになれば、データの準備で困ることはなくなります。 次は、もっと柔軟な「リスト」との付き合い方や、最新の「ストリーム」との連携についてお話しします。


第4章 柔軟性を手に入れる!リスト変換とストリーム連携

概要

実務では、配列のまま扱うよりも「List」という可変長の入れ物に入れ替えた方が便利な場面がたくさんあります。 また、Java 8から登場した「Stream」を使えば、大量のデータから特定の条件に合うものだけを抜き出すといった処理が驚くほど短く書けます。 配列を「ただの箱」から「流れるデータ」へと進化させましょう。

対象メソッド

  • Arrays.asList
  • Arrays.stream

サンプルコード

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;

public class ArrayModernMaster {
    public static void main(String[] args) {
        String[] array = {"Java", "Python", "Ruby"};
        List<String> list = Arrays.asList(array);
        System.out.println("1: " + list);

        int[] scores = {45, 80, 65, 90, 55};
        long highScoresCount = Arrays.stream(scores)
                                     .filter(n -> n >= 70)
                                     .count();
        System.out.println("2: 70点以上の人数は " + highScoresCount + " 名");
    }
}

実行結果

1: [Java, Python, Ruby] 2: 70点以上の人数は 2 名

逐次解説

  1. String配列を、Arrays.asListを使ってList形式に変換しています。これで、Listを引数に取る他の便利なメソッドにデータを渡せるようになります。
  2. Arrays.stream(scores)で、配列の中身を「データの流れ(ストリーム)」に変換します。
  3. .filter(n -> n >= 70)の部分で、70以上のデータだけをふるいにかけます。
  4. 最後に.count()で、生き残ったデータの数を数えています。

イメージ

Arrays.asListは、配列という「木の箱」を、Listという「魔法の袋」に詰め替えるイメージです。ただし、この袋は元の箱と繋がっていて、中身を書き換えると両方に影響が出る「共有」状態にあります。 Arrays.streamは、箱に入ったジャガイモ(データ)をベルトコンベアに乗せるようなものです。コンベアの上で「小さいのは捨てる」「形を整える」といった加工を連続して行い、最後にまとめて出荷する……という一連の流れ(パイプライン)を作ることができます。

よくある誤解

Arrays.asListで作ったリストに対して、list.add("PHP")のように要素を追加しようとするとエラー(UnsupportedOperationException)が発生します! 「えっ、Listはサイズが自由なはずでは?」と思うかもしれませんが、asListで作ったリストは「元の配列のサイズ」に縛られた特殊なリストなのです。 本当の意味で自由なリストにしたいなら、new ArrayList<>(Arrays.asList(array)) のように新しく作り直す必要があります。

実務での重要性

現代のJava開発において、for文で1つずつif文を使ってデータを仕分けるコードは減りつつあります。 Arrays.streamを使いこなせれば、複雑な集計処理もたった数行で、しかも読みやすく記述できます。 「配列でデータを受け取り、Streamで加工して、Listで返す」という流れは、実務の現場で嫌というほど登場する王道パターンですよ!

演習問題

問1:Arrays.asListで変換したリストの要素を書き換えたとき、元の配列の中身はどうなるでしょうか?

問2:Arrays.streamを使って、int配列 {1, 2, 3, 4, 5} の「合計値」を求めるには、.count() の代わりに何のメソッドを使えばよいでしょうか?


いよいよ配列の扱いもプロ級に近づいてきましたね。 最後は、配列の設計上の制約や、メソッドの引数として配列をスマートに渡す「可変長引数」の正体に迫ります。


第5章 配列の限界を知る!固定長設計と可変長引数の正体

概要

Javaの配列は、生成した瞬間に「箱の数」が決まってしまい、後から増やしたり減らしたりできません。これを「固定長」と呼びます。 この章では、その制約をどう受け入れるか、そしてメソッドに引数を渡す際に配列を自動で作ってくれる「可変長引数」という魔法のような仕組みを学びます。

対象メソッド・構文

  • 可変長引数(String... args)
  • 配列の length プロパティ

サンプルコード

public class ArrayFinalMaster {
    public static void main(String[] args) {
        // 可変長引数を使ったメソッドの呼び出し
        printMessages("こんにちは", "Javaの世界へ", "ようこそ!");
        
        // 配列を直接渡すこともできる
        String[] array = {"さようなら", "また明日"};
        printMessages(array);
    }

    // 可変長引数(...)の定義
    static void printMessages(String... messages) {
        // メソッド内部では「messages」はただの配列として扱える
        System.out.println("受け取った要素数: " + messages.length);
        for (String msg : messages) {
            System.out.print(msg + " ");
        }
        System.out.println();
    }
}

実行結果

受け取った要素数: 3 こんにちは Javaの世界へ ようこそ! 受け取った要素数: 2 さようなら また明日

逐次解説

  1. printMessages メソッドの引数にある「String...」が可変長引数です。
  2. 呼び出し側では、カンマ区切りで好きなだけ引数を並べることができます。Javaが裏側で勝手に配列にまとめてくれています。
  3. もちろん、既に手元にある配列をそのまま放り込むことも可能です。
  4. メソッドの中に入ってしまえば、messages は String[] 型の配列として扱えます。
  5. 配列のサイズを知るには length を使いますが、これはメソッドではなく「変数(プロパティ)」なので、カッコ () は不要です。

イメージ

可変長引数は、「セルフサービスのお惣菜コーナー」をイメージしてください。 あなたが好きなだけおかず(引数)をお皿に乗せると、レジ(Javaのコンパイラ)が自動的にそれを一つの「パック(配列)」に詰めて、厨房(メソッド内部)へ運んでくれる仕組みです。 「あらかじめパックを用意してから渡す」という手間が省けるので、コードがとてもスッキリします!

よくある誤解

「配列のサイズを後から変えたいときは length = 10; のように書き換えればいい」と勘違いしがちですが、これは不可能です! length は読み取り専用の定数のようなものです。サイズを変えたい場合は、第3章でやったように copyOf を使って「新しい大きな箱」に引っ越すしかありません。 もし頻繁にサイズを変える必要があるなら、最初から ArrayList を使う設計にすべき、というサインです。

実務での重要性

可変長引数は、ログを出力するメソッドや、複数の条件を指定してデータを検索するメソッドなどで非常によく使われます。 「呼び出し側が配列を new する手間を減らしてあげる」という優しさが、ライブラリや共通部品を作るエンジニアには求められます。 また、固定長である配列はメモリ効率が良いため、大量のデータを高速に処理する「土台」として、今でもプロの現場で重宝されています。

演習問題

問1:可変長引数を持つメソッドを定義するとき、他の引数(int型など)と混ぜて使う場合に守らなければならない「引数の順番」のルールは何でしょうか?

問2:length と、Listなどで使う size() の違いを、一言で説明してみてください。


今後の学習の指針

全5章、お疲れ様でした!Arraysクラスを使いこなせるようになったあなたは、もう配列操作で怖気づくことはありません。 これからのステップアップとして、以下の順序で学習を広げていくことをおすすめします!

  • コレクションフレームワークの深掘り: 配列の進化系である ArrayList や、重複を許さない Set、キーと値で管理する Map を使い分けられるようになりましょう。
  • ラムダ式とStream API: 第4章で触れた Stream をより詳しく学ぶと、Javaのコードがさらにモダンで美しくなります。
  • 計算量(アルゴリズム)の意識: Arrays.sort がなぜ速いのか、 binarySearch はなぜソートが必要なのかを「計算量」という視点で調べると、一段上のエンジニアになれます。

配列はすべてのデータ構造の基本です。ここをしっかり固めたあなたは、どんな新しい技術もスムーズに吸収できるはず。 これからも一緒に楽しくコードを書いていきましょう!

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。