Java超|多次元配列で表データを扱う

配列を表にできると、データの見え方が変わる。
多次元配列を覚えると、Javaで「行」と「列」を持つ記録表をきれいに扱えるようになる。

これまで学んできた配列は、横一列にデータを並べる形が中心でした。

たとえば、戦士たちの修行点数、1週間分の訓練時間、補給品の価格一覧、仙豆畑の水やり回数などです。

int[] score = {70, 85, 60};

このような配列は、1つの方向にデータが並んでいます。

添字値
070
185
260

この形は、点数一覧や価格一覧のように、横一列で考えられるデータに向いています。

ただし、実際のプログラムでは、横一列だけでは表しにくいデータもあります。

たとえば、次のようなデータです。

表したいデータ行と列のイメージ
戦士 × 訓練科目の点数戦士ごと、科目ごとの点数
補給品 × 月ごとの在庫補給品ごと、月ごとの数
任務班 × 日ごとの達成数班ごと、日ごとの記録
バトル訓練の表データ行と列で整理された数値
マップ情報縦位置と横位置

このように、行と列で整理したいデータを扱うときに便利なのが、多次元配列です。

特に、2次元配列は「表」のようなデータを扱うときによく使います。

ドラゴンボール風にたとえると、1次元配列は「戦士たちの修行記録札が横一列に並んだ棚」です。

一方、2次元配列は「訓練科目ごと、戦士ごとに点数を整理した巨大な修行成績表」です。

たとえば、行に「気功訓練」「瞬間移動訓練」を並べ、列に「1人目」「2人目」「3人目」の戦士を並べれば、どの戦士がどの訓練で何点だったのかを表として管理できます。

この記事では、2次元配列を中心に、多次元配列のしくみ、添字の意味、初期化、行ごとに長さが違う配列、length の使い分けまでを、Sample11.java と Sample12.java を使って整理していきます。

多次元配列とは何か

多次元配列は、配列の中にさらに配列が入っている構造です。

これまでの1次元配列は、値が横一列に並ぶ形でした。

10  20  30  40

これは、1つの方向にだけデータが並んでいるイメージです。

一方、2次元配列では、次のように行と列を持つ形になります。

10  20  30
40  50  60

表で見ると、次のようになります。

行列0列1列2
0102030
1405060

2次元配列では、1つ目の添字で行を指定し、2つ目の添字で列を指定します。

配列名[行][列]

たとえば、data[0][1] なら、0行目の1列目を表します。

書き方意味
data[0][0]0行目の0列目
data[0][1]0行目の1列目
data[1][0]1行目の0列目
data[1][2]1行目の2列目

ドラゴンボール風にたとえると、2次元配列は「修行成績表」です。

1つ目の番号で「どの訓練科目か」を選びます。
2つ目の番号で「どの戦士の記録か」を選びます。

つまり、表の中から「どの行の、どの列の値か」を指定するのが2次元配列です。

1次元配列と2次元配列の違い

1次元配列と2次元配列の違いを整理すると、次のようになります。

種類イメージ向いているデータ
1次元配列横一列のデータ点数一覧、気温一覧、価格一覧
2次元配列行と列のある表科目別点数、月別売上、表形式データ

1次元配列では、1つの番号で要素を指定します。

data[0]
data[1]
data[2]

2次元配列では、2つの番号で要素を指定します。

data[0][0]
data[0][1]
data[1][0]
書き方意味
data[0]1次元配列の0番目、または2次元配列の0行目
data[0][0]2次元配列の0行0列
data[1][2]2次元配列の1行2列

ここで大切なのは、2次元配列では「値の場所」を行と列で指定するということです。

1次元配列は、番号を1つ指定すれば値にたどり着けます。

一方、2次元配列では、まずどの行かを選び、次にその行のどの列かを選びます。

ドラゴンボール風にたとえると、1次元配列は「戦士全員の点数札が横一列に並んでいる棚」です。

2次元配列は「訓練科目ごとに行が分かれ、戦士ごとに列が分かれた成績表」です。

横一列だけではなく、縦と横の位置を使ってデータを整理できるのが、2次元配列の大きな特徴です。

2次元配列の基本的な書き方

2次元配列を宣言するときは、型名のあとに [] を2つ付けます。

型名[][] 配列名;

int型の2次元配列なら、次のようになります。

int[][] data;

これは、整数を表形式でまとめて扱うための配列変数を用意する書き方です。

次に、要素を確保するときは、行数と列数を指定します。

配列名 = new 型名[行数][列数];

たとえば、3行4列の配列を作るなら、次のようになります。

int[][] data;
data = new int[3][4];

この場合、3行4列なので、合計で12個の値を入れられます。

行数列数合計要素数
3412

3 × 4 = 12 なので、表の中に12個の箱があるイメージです。

行列0列1列2列3
0箱箱箱箱
1箱箱箱箱
2箱箱箱箱

ドラゴンボール風にたとえると、3つの訓練ステージがあり、それぞれに4人分の記録欄がある修行成績表を作るようなものです。

行が訓練ステージ。
列が戦士。
表の1つ1つの箱に点数や記録を入れていきます。

2次元配列では添字を2つ使う

2次元配列で特に大切なのは、添字を2つ使うことです。

score[0][0]
score[0][1]
score[1][0]

このような書き方では、1つ目の添字と2つ目の添字に、それぞれ役割があります。

添字役割
1つ目の添字行を表す
2つ目の添字列を表す

たとえば、score[1][2] なら、1行目の2列目を表します。

書き方意味
score[0][0]0行目の0列目
score[0][1]0行目の1列目
score[1][0]1行目の0列目
score[1][2]1行目の2列目

Javaの添字は0から始まります。

そのため、人間向けには「1人目」「2人目」と表示していても、コード上では0、1、2のように数えます。

たとえば、3人分のデータなら列の添字は次のようになります。

人間向けの数え方Javaの列添字
1人目0
2人目1
3人目2

ドラゴンボール風にたとえると、1つ目の添字は「どの訓練科目か」を表します。
2つ目の添字は「どの戦士か」を表します。

score[0][2] なら、0行目の訓練科目における3人目の戦士の点数です。

図:2次元配列は行と列で値を管理する

この図が示していること

この図では、2次元配列 score が2行3列の表としてデータを管理している様子を示しています。

行0は気功訓練、行1は瞬間移動訓練を表しています。
列0、列1、列2は、それぞれ1人目、2人目、3人目の戦士を表しています。

たとえば、score[0][0] は 気功訓練・1人目 の点数で、値は70です。

score[1][2] は 瞬間移動訓練・3人目 の点数で、値は90です。

ここから分かることは、2次元配列では1つ目の添字で行を選び、2つ目の添字で列を選ぶということです。

配列を表のように見ると、多次元配列のしくみがかなりイメージしやすくなります。

2次元配列を使う

ここでは、2つの訓練科目と3人の戦士の点数を、2次元配列で管理します。

ファイル名:Sample11.java

class Sample11
{
    public static void main(String[] args)
    {
        int[][] score;
        score = new int[2][3];   // 訓練科目 × 戦士数の配列を準備

        // 気功訓練の点数
        score[0][0] = 70;
        score[0][1] = 85;
        score[0][2] = 60;

        // 瞬間移動訓練の点数
        score[1][0] = 80;
        score[1][1] = 75;
        score[1][2] = 90;

        for(int i = 0; i < 3; i++){
            System.out.println((i + 1) + "人目の気功訓練は " +
                               score[0][i] + "点です。");
            System.out.println((i + 1) + "人目の瞬間移動訓練は " +
                               score[1][i] + "点です。");
        }
    }
}

実行結果は次のようになります。

1人目の気功訓練は 70点です。
1人目の瞬間移動訓練は 80点です。
2人目の気功訓練は 85点です。
2人目の瞬間移動訓練は 75点です。
3人目の気功訓練は 60点です。
3人目の瞬間移動訓練は 90点です。

このプログラムでは、score という2次元配列を使っています。

int[][] score;
score = new int[2][3];

これは、2行3列の int型配列を作っているという意味です。

行意味列0列1列2
0気功訓練708560
1瞬間移動訓練807590

行0には気功訓練の点数を入れています。
行1には瞬間移動訓練の点数を入れています。

列は戦士の番号に対応しています。

列意味
01人目
12人目
23人目

つまり、score[0][1] は2人目の気功訓練、score[1][2] は3人目の瞬間移動訓練を表します。

score[0][i] と score[1][i] の意味

Sample11.java の表示部分では、for文で i を0から2まで変化させています。

for(int i = 0; i < 3; i++){
    System.out.println((i + 1) + "人目の気功訓練は " +
                       score[0][i] + "点です。");
    System.out.println((i + 1) + "人目の瞬間移動訓練は " +
                       score[1][i] + "点です。");
}

ここで、score[0][i] は気功訓練の点数を表します。

score[1][i] は瞬間移動訓練の点数を表します。

i の値score[0][i]意味score[1][i]意味
0701人目の気功訓練801人目の瞬間移動訓練
1852人目の気功訓練752人目の瞬間移動訓練
2603人目の気功訓練903人目の瞬間移動訓練

このように、1つ目の添字を固定して、2つ目の添字だけをループで変化させると、同じ訓練科目の中で戦士ごとの点数を順番に取り出せます。

ドラゴンボール風にたとえると、score[0][i] は「気功訓練の成績表の中から i番目の戦士を見る」動きです。

score[1][i] は「瞬間移動訓練の成績表の中から i番目の戦士を見る」動きです。

i が0なら1人目、i が1なら2人目、i が2なら3人目の記録を見ています。

2次元配列はまとめて初期化できる

2次元配列も、1次元配列と同じように、値をまとめて初期化できます。

たとえば、次のように書けます。

int[][] score = {
    {70, 85, 60},
    {80, 75, 90}
};

この書き方では、波かっこの中に、さらに波かっこが入っています。

行中身意味
0{70, 85, 60}気功訓練の点数
1{80, 75, 90}瞬間移動訓練の点数

外側の波かっこは、2次元配列全体を表します。

内側の波かっこは、各行の配列を表します。

ドラゴンボール風にたとえると、外側の帳面が修行成績表全体です。

内側の行が「気功訓練の行」「瞬間移動訓練の行」になっています。

この書き方を使うと、表の中身をまとめて見やすく書けます。

多次元配列は配列の中に配列がある構造

2次元配列は、表のように見えるため、最初は「1つの大きな表」と考えると分かりやすいです。

ただ、Javaのしくみとしては、配列の中に配列が入っている構造です。

たとえば、次のような2次元配列を考えます。

int[][] score = {
    {70, 85, 60},
    {80, 75, 90}
};

これは、次のようなイメージです。

score の中中身
score[0]{70, 85, 60}
score[1]{80, 75, 90}

つまり、score[0] 自体が1つの配列です。
score[1] も1つの配列です。

そこからさらに score[0][1] のように書くと、score[0] の中の1番目の要素を取り出せます。

書き方意味
score[0]0行目の配列
score[0][1]0行目の配列の1番目の要素
score[1]1行目の配列
score[1][2]1行目の配列の2番目の要素

この考え方が分かると、次に出てくる「行ごとに長さが違う配列」も理解しやすくなります。

ドラゴンボール風にたとえると、score は「修行成績表全体」です。

score[0] は「気功訓練の点数が並んだ1行」です。
score[1] は「瞬間移動訓練の点数が並んだ1行」です。

さらに score[0][1] と書くと、「気功訓練の行の中にある2人目の点数」を取り出しているわけです。

行ごとに長さが違う配列も作れる

Javaの多次元配列では、すべての行の長さが同じでなくてもかまいません。

たとえば、次のような配列を作れます。

int[][] data = {
    {10, 20, 30},
    {40, 50},
    {60, 70, 80, 90}
};

この配列では、行ごとの要素数が違います。

行要素要素数
010, 20, 303
140, 502
260, 70, 80, 904

これは、2次元配列が「配列の中に配列がある構造」だからです。

data[0] や data[1] のような各行は、それぞれ別の配列として考えられます。

そのため、行ごとの要素数が違っていても、1つの2次元配列としてまとめられます。

ドラゴンボール風にたとえると、任務班ごとに参加人数が違うようなものです。

第1班は3人。
第2班は2人。
第3班は4人。

それぞれの班の人数が違っても、1つの任務記録表としてまとめて管理できます。

length の使い方が1次元配列とは少し変わる

多次元配列では、length の使い方がとても大切です。

1次元配列では、配列名.length は要素数を表しました。

2次元配列では、次のように考えます。

書き方意味
data.length行の数
data[i].lengthi行目の列の数、つまりi行目の要素数

この違いは、とても重要です。

たとえば、次の配列を考えます。

int[][] data = {
    {10, 20, 30},
    {40, 50},
    {60, 70, 80, 90}
};

この場合、data.length は 3 です。

なぜなら、data の中には3つの行があるからです。

一方、data[0].length は 3、data[1].length は 2、data[2].length は 4 です。

式値意味
data.length3行の数
data[0].length30行目の要素数
data[1].length21行目の要素数
data[2].length42行目の要素数

ドラゴンボール風にたとえると、data.length は「任務班が何班あるか」を表します。

data[i].length は「i番目の班に戦士が何人いるか」を表します。

班の数と、各班の人数は別のものです。

ここを分けて考えると、多次元配列の length が理解しやすくなります。

図:多次元配列の length は行数と列数で使い分ける

この図が示していること

この図では、data 配列が3つの行を持ち、行ごとに要素数が違う様子を示しています。

data.length は、行の数を表すので 3 です。

data[0].length は、0行目の要素数なので 3 です。
data[1].length は、1行目の要素数なので 2 です。
data[2].length は、2行目の要素数なので 4 です。

ここから分かることは、多次元配列では length をどこに付けるかによって意味が変わるということです。

配列全体に付ける length は行数を表し、各行に付ける length はその行の要素数を表します。

行ごとの長さを確認する

ここでは、行ごとに要素数が違う2次元配列を作り、それぞれの行の長さを表示します。

ファイル名:Sample12.java

class Sample12
{
    public static void main(String[] args)
    {
        int[][] data = {
            {10, 20, 30},
            {40, 50},
            {60, 70, 80, 90}
        };

        for(int i = 0; i < data.length; i++){
            System.out.println((i + 1) +
                "行目の任務記録数は " + data[i].length + " 個です。");
        }
    }
}

実行結果は次のようになります。

1行目の任務記録数は 3 個です。
2行目の任務記録数は 2 個です。
3行目の任務記録数は 4 個です。

このプログラムでは、data という2次元配列をまとめて初期化しています。

int[][] data = {
    {10, 20, 30},
    {40, 50},
    {60, 70, 80, 90}
};

この配列は、3行あります。

行要素数
03
12
24

for文では、data.length を使って、行の数だけ繰り返しています。

for(int i = 0; i < data.length; i++){
    System.out.println((i + 1) +
        "行目の任務記録数は " + data[i].length + " 個です。");
}

data.length は 3 なので、i は 0、1、2 と変化します。

i の値data[i].length表示される内容
031行目の任務記録数は 3 個です。
122行目の任務記録数は 2 個です。
243行目の任務記録数は 4 個です。

このように、data[i].length を使うと、行ごとの要素数を確認できます。

ドラゴンボール風にたとえると、data.length で「任務班が全部で何班あるか」を確認します。

そして、data[i].length で「その班に何人分の記録があるか」を確認します。

data.length と data[i].length の違いをしっかり分ける

多次元配列では、data.length と data[i].length の違いがとても大切です。

書き方見ているもの
data.lengthdata に何行あるか
data[i].lengthi行目に何個の要素があるか

この違いを混同すると、ループの条件を間違えやすくなります。

たとえば、行ごとに長さが違う配列では、すべての行に同じ列数があるとは限りません。

int[][] data = {
    {10, 20, 30},
    {40, 50},
    {60, 70, 80, 90}
};

この場合、1行目、つまり data[1] には2個しか要素がありません。

そのため、data[1][2] は存在しません。

指定存在するか
data[1][0]存在する
data[1][1]存在する
data[1][2]存在しない

行ごとに長さが違う場合は、内側のループで data[i].length を使うことがとても大切です。

もし、すべての行に同じ列数があると思い込んで固定の数字で処理すると、存在しない列にアクセスしてしまう可能性があります。

ドラゴンボール風にたとえると、第2班には2人しかいないのに、3人目の記録を見ようとしている状態です。

そこには記録札がありません。

多次元配列を全部処理するときの基本形

2次元配列のすべての要素を処理したいときは、二重の for文 を使うことが多いです。

基本の考え方は次の通りです。

for(int i = 0; i < data.length; i++){
    for(int j = 0; j < data[i].length; j++){
        data[i][j] を使った処理;
    }
}

ここで、外側の i は行を表します。
内側の j は列を表します。

変数役割
i行を進める
jその行の中の列を進める

外側の for文 では、data.length を使って行数ぶん繰り返します。

内側の for文 では、data[i].length を使って、その行の要素数ぶん繰り返します。

これにより、行ごとに長さが違っても、安全に要素を処理できます。

たとえば、次の data なら、

int[][] data = {
    {10, 20, 30},
    {40, 50},
    {60, 70, 80, 90}
};

処理の流れは次のようになります。

i見る行j の範囲処理される要素
0data[0]0, 1, 2data[0][0]〜data[0][2]
1data[1]0, 1data[1][0]〜data[1][1]
2data[2]0, 1, 2, 3data[2][0]〜data[2][3]

ドラゴンボール風にたとえると、外側の i は「どの班を見るか」を決めます。

内側の j は「その班の何人目を見るか」を決めます。

班ごとに人数が違う場合でも、data[i].length を使えば、その班の人数ぶんだけ確認できます。

図:二重のfor文で多次元配列を安全に処理する

この図が示していること

この図では、二重の for文 を使って、多次元配列 data の全要素を安全に処理する流れを示しています。

外側の for文 では、i が 0、1、2 と変化し、どの行を見るかを決めます。

内側の for文 では、j がその行の中を左から右へ進みます。

ここで大切なのは、内側の条件に data[i].length を使っていることです。

data[0] は3個、data[1] は2個、data[2] は4個の要素を持っています。
それぞれの行の長さに合わせて j の範囲が変わるため、存在しない要素へ進みにくくなります。

ここから分かることは、多次元配列を処理するときは、外側で行数、内側でその行の要素数を見ることが大切だということです。

2次元配列が向いている場面

2次元配列は、行と列で整理できるデータに向いています。

場面行列
戦士ごとの訓練科目別点数訓練科目戦士
補給品ごとの月別在庫補給品月
任務班ごとの日別記録班日
表形式の数値データ行列
ゲームのマップ縦位置横位置

1次元配列では、横一列のデータしか表しにくいです。

でも2次元配列を使えば、縦と横のまとまりを持つデータを自然に表現できます。

ドラゴンボール風にたとえると、1次元配列は「戦士全員の点数一覧」です。

2次元配列は「訓練科目ごとに分かれた戦士全員の成績表」です。

配列の種類ドラゴンボール風のイメージ
1次元配列戦士の点数札が横一列に並んだ棚
2次元配列訓練科目ごと、戦士ごとに整理された成績表

表として見たいデータには、2次元配列がとても向いています。

多次元配列で気をつけたいこと

多次元配列を使うときは、次の点に注意すると安心です。

注意点内容
添字が2つあるどちらが行で、どちらが列かを意識する
添字は0から始まる1行目はコード上では行0
data.length は行数全体の行数を見る
data[i].length は列数i行目の要素数を見る
行ごとに長さが違うことがあるすべての行が同じ列数とは限らない
範囲外アクセスに注意存在しない列を指定しない

特に、data.length と data[i].length の違いはしっかり押さえておきたいところです。

すべての行が同じ長さなら、表のように考えやすいです。

しかし、Javaでは行ごとに長さが違う多次元配列も作れます。

そのため、「全体の行数」と「各行の要素数」を分けて見ることが大切です。

ドラゴンボール風にたとえると、任務表全体には3班あります。
でも、第1班は3人、第2班は2人、第3班は4人かもしれません。

班の数と、各班の人数を同じものとして扱うと、存在しない記録札を見に行ってしまうことがあります。

多次元配列は配列学習の応用につながる

多次元配列を理解すると、配列で扱えるデータの幅が大きく広がります。

これまでは、1列のデータを順番に扱っていました。

そこに行と列の考え方を加えることで、表形式のデータを扱えるようになります。

学んだことできるようになること
1次元配列横一列のデータを扱う
2次元配列表のようなデータを扱う
2つの添字行と列を指定する
多次元配列の初期化表データをまとめて書く
data.length行数を確認する
data[i].length行ごとの要素数を確認する

ドラゴンボール風にたとえると、多次元配列は「大きな修行記録表」です。

1つの列だけでなく、訓練科目、戦士、班、日付などを組み合わせて整理できます。

この考え方が身につくと、科目別成績、売上表、記録表、マップ情報など、より実践的なデータをJavaで扱いやすくなります。

多次元配列は表データを扱うための強力な道具

多次元配列を使うと、配列を表のように扱えます。

1次元配列では、データは横一列に並んでいました。

2次元配列では、そこに行と列の考え方が加わります。

配列名[行][列]

この形を理解できると、「どの行の、どの列の値か」を指定してデータを扱えるようになります。

また、行ごとに長さが違う配列も作れるため、Javaの多次元配列は柔軟です。

その分、data.length と data[i].length の違いを意識することが大切です。

書き方意味
data.length行の数
data[i].lengthi行目の要素数

配列の考え方に、もう1つ次元を加える。

それが多次元配列です。

ドラゴンボール風にたとえると、横一列の修行記録札だけでなく、訓練科目ごと、班ごと、日ごとに整理された大きな記録表を扱えるようになるイメージです。

表形式のデータを扱えるようになると、Javaで表現できるデータの幅がぐっと広がります。