配列

配列

配列の説明に入る前に基本型(リテラル)についておさらいしておきましょう。
基本形(リテラル)では1つの変数を定義して、その変数に対して値を代入します。

// 変数sportsに値"野球"を入れる
String sports = "野球";

配列(Array)の場合は、わかりやすく言うとこの変数の値が複数格納できるものになります。

このように複数の値が格納できるため、大量のデータを扱いたいときに配列は非常に便利です。
イメージとして例えるのであれば電車を連想するといいかもしれません。
電車は車両と車両が連結されており、それぞれに人が乗っています。配列も電車のように連結されており、情報を保持しておくことが可能です。

また、配列には連想配列、多次元配列があります。
連想配列はキーと値を別々に指定できる配列、多次元配列は配列の内部に配列を持つことができます。
それぞれの使い方についてもこの後、解説していきます。
プログラムをする上で配列を覚えると効率的なコードを書けるようになります。ぜひマスターするようにしましょう。

つまり配列とは
・一つの変数に値を複数格納できる
・配列には連想配列と多次元配列という種類もある

まずはいままでの書き方で説明していきます。スポーツを3つ変数に格納する場合、以下のように書いていたと思います。
「com.cmps.array」パッケージを作成し、その中に「Array」クラスを作成してください。

public static void main(String[] args) {
        String sports1 = "野球";
        String sports2 = "サッカー";
        String sports3 = "テニス";

        System.out.println(sports1);
        System.out.println(sports2);
        System.out.println(sports3);
}

これを配列で書いた場合

        String[] sports;
        sports = new String[3];

        sports[0] = "野球";
        sports[1] = "サッカー";
        sports[2] = "テニス";

        System.out.println(sports[0]);
        System.out.println(sports[1]);
        System.out.println(sports[2]);

同じ結果が表示されたと思います。
このように一つの変数で複数の値を格納することができます。

配列の書き方

宣言

配列の名前の付け方は、通常の変数と同じです。但し、宣言の仕方が少し異なります。
上記の例では、以下の箇所が該当します。

 String[] sports;

String型の右に「[]」がついています。この[ ]を付けることでその文字を配列としてみなすことが出来ます。
配列を宣言するには以下の構文を使うことで使用可能です。

型名 [] 配列変数名;

配列の宣言は、配列を管理する変数を作るだけです。
配列の宣言をした後にはデータを記憶する領域を確保する必要があります。
ここで言う「データを記憶する領域」とは、配列の実体(実際の値)が入る領域(場所)を意味します。
具体的には、パソコンのメモリの中にデータを記憶する場所を確保することです。

sports = new String[3];

構文としては以下のようになります。

配列変数名 = new 型名[要素数];

new演算子は、配列などのオブジェクトと呼ばれるものを新しく作成する場合に使用する演算子です。
new演算子により、配列の実体の領域が確保されます。
要素数は文字通り要素の数です。例では、String型のデータ3個分(要素数が3)の記憶領域を確保しています。
なお、配列の大きさ(要素数)は、一度記憶領域を確保すると変更は出来ません。

また、変数の宣言と、配列の要素数の確保を同時に行う事も可能です。

型名[] 配列変数名 = new 型名[要素数];

上記の例で記述すると、このようになります。

String[] sports = new String[3];

これで、配列変数sportsが、実際に値が入っている場所を参照するようになります。
配列変数sportsに代入されている値は、配列の実体である要素(ここではsports[0])がメモリ上に記憶されている場所情報)です。

配列への格納
sports[0] = "野球";

配列に値を入れるときは宣言した配列の[]部分に何個目に値を入れるかを指定する必要があります。
構文は以下の通りです。

配列変数名[インデックス] = 値;

この時、指定に使う言葉をインデックスと呼びます。

今回の処理では「sports[0]」~「sports[2]」までの指定をした後に値を代入しており、この0~2までの数字がインデックスになります。
なお、配列の要素のインデックスは、「1」ではなく必ず「0」から数えられるということに注意しましょう。
日常生活で物を数えるときは1から数えることが多いですが、プログラムでは0から数えることが原則です(例外の言語もあるが少数)。

配列の初期化

要素の既定値

変数の場合、宣言してから一度も値を代入しない状態で変数参照すると、コンパイル時にエラーとなります。
配列の場合、配列を作成した時点でそれぞれの要素にはデータ型に応じた規定値が代入されています。
以下のコードを出力してみましょう。

int[] num = new int[2];
        
System.out.println(num[0]);
System.out.println(num[1]);

どちらも0が出力されたと思われます。
配列の宣言後、各要素に値を格納しないまま出力をしていますが、エラーとはなっていません。

要素の規定値は、数値型の場合はそれぞれ 0 が代入され、 boolean 型の場合は false 、 String 型など参照型のデータ型の場合は null となっています。

以下はString型を初期化する際のコードです。

// String型の初期化
String[] testString = new String[2];

System.out.println(testString[0]);

String 型の場合は、すべての要素が null で初期化されます。

要素を任意の値で初期化

配列を宣言する際、任意の値で初期化することも可能です。
その場合は、下記のような構文となります。

型名[] 配列変数名 = {値, 値,・・・};

指定した値の個数分の配列の要素が確保され、その値で初期化されます。
この構文は、new演算子を使って要素の記憶領域の確保を行い、その後に値を代入する先ほどの書き方と同じです。
なお、配列変数の宣言と任意の値での初期化は、必ず同時に行わなければなりません

以下のソースを記述してみましょう

int[] number = {2,20,200};

System.out.println(number[0]);
System.out.println(number[1]);
System.out.println(number[2]);

int型の値を格納する配列を作成し、3つの要素を持つ配列を作成した上で、それぞれに2,20,200を代入しています。
これは次のような記述と、行っていることは同じです。

int[] number = new int[3];
        
number[0] = 2;
number[1] = 20;;
number[2] = 200;

配列の操作

配列の合計

IntStreamのsum()メソッドを使うことで、配列の合計を求めることが出来ます。
IntStreamとは数を扱うための様々な機能が備わったものです。

以下のように記述します。

IntStream.of(配列変数).sum();
配列の長さ

配列変数に対してlengthを使うと、その配列の長さ(配列の要素数)を取得できます。

配列変数.length

合計と長さを使い、配列の平均値を求めることも可能です。

上記のメソッドなどを利用して、配列を操作してみましょう。

int[] test = {1000,100,10,1,0};
        
// 配列の合計
int sum = IntStream.of(test).sum();
System.out.println("合計:" + sum);
        
// 配列の長さ
int length = test.length;
System.out.println("長さ:" + length);
        
// 合計と長さを使い平均を求める
int avg = sum / length;
System.out.println("平均:" + avg);

配列の文字列表現の取得

配列を作成し配列変数に代入すると、配列変数には配列への参照が格納されます。
その為、配列変数をそのまま画面に出力などすると、参照を表す文字列が表示されます。

int[]  src = {30,50,80,90};

System.out.println(src); // [I@3e3abc88

配列の要素に格納されている値を使って [値1, 値2, …] のような形式の文字列を取得するには Arrays.toString メソッドを使用します。
toString はクラスメソッドとして定義されており、引数の型毎に異なるメソッドが定義されています。

Arrays.toString(配列変数)

上で作成したsrcの中身をArray.toStringを使用して出力してみましょう。

System.out.println(Arrays.toString(src));

[30, 50, 80, 90]と配列の値が表示されました。

多次元配列

多次元配列とは、一言で言うと配列の中にさらに複数の配列が入っているものになります。

今までの配列は、配列の要素が一方向に一列に繋がっていました。このような配列を特に1次元配列といいます。
配列の各要素からも要素(の列)が伸びる2次元配列や3次元の方向にも要素を持つ3次元配列、
あるいはそれ以上の次元にも要素を持つような配列を作ることも可能です

2次元配列の宣言については以下の構文で作成可能です。

型名[][] 配列名 = new 型名[1次元(行)要素数][2次元(列)要素数];

1番目に指定した要素数が1次元方向(行)の要素数で、2番目に指定した要素数が2次元方向(列)の要素数です。

以下は九九のデータを格納した2次元配列です。

// 配列の宣言
int[][] graph = new int[9][9];
        
// 二重for文で配列変数graphに全ての九九の解答を格納
for(int dan = 0;dan < 9;dan++) {
 for(int value = 0;value < 9;value++) {
  graph[dan][value] = (dan + 1) * (value + 1);
  }
 }

// 配列変数の全ての要素を表示
for(int dan = 0; dan < 9; dan++) {
 System.out.print((dan + 1) + "の段 : ");
 for(int i = 0; i < 9; i++) {
  System.out.print(graph[dan][i] + "\t");
 }
 System.out.println();
}

出力結果としては以下のようになるかと思われます。

5行目から14行目にかけてfor文を使い、配列の中に九九の計算結果を格納しています。
カウンタ変数であるdanとvalueは、そのままでは要素のインデックスとして利用されてますが、
それぞれに1を足すことで、1回目のfor文で用いたカウンタ変数danは、
段数を表し、2回目のfor文で用いたvalueは、かける数を表します。

ちなみに System.out.print(graph[dan][i] + "\t");にある「\t」ですが
これは「水平タブ文字」を表し、テキスト出力において一定のスペースを開けてくれます。

多次元配列の文字列表現の取得

多次元配列に格納されている要素の値を [値1, 値2, …] のような形式の文字列を取得するには、
Arrays deepToStringメソッドを使用します。書き方はArrays.toStringと同様です。

以下のソースを記述してみましょう。

int[][] deep = {{1,100},{2,200},{3,300}};

System.out.println(Arrays.deepToString(deep)); // [[1, 100], [2, 200], [3, 300]]

1番目の引数に対象の多次元配列を指定すると、配列の文字列表現を返します。

基本データ型(intやchar、booleanなど)の1次元配列の場合、deepToStringメソッドを使用するとコンパイルエラーとなりますので、1次元配列の場合はtoStringメソッドを使用しましょう。

List

Listとは

Listとは、Javaで使用できる複数のデータを1つにまとめられるものです。
出来ることは配列と似ていますが、明確な違いは「箱の数の増減方法」です。

配列を使用する時は、予め配列で使用する箱の数を指定する必要があります。
例えば上記で使用した以下のコードでは、sportsに要素を3つ入れることが可能です。

String[] sports = new String[3];

しかし、新たに要素を追加したくても、要素数を一度定義してしまっているのでそれが出来ません。

そこで利用するのがListです。
Listは一度作った後でも、追加で更にデータを入れることが出来るのが配列との大きな違いです。

最初にListを使って宣言すると、(List <String> sports = new ArrayList <String> (); )理論上無限に入る箱を作ることが出来ます。

Listはインターフェースという抽象化された概念であり、実際にListを使用する際は、
クラスとして実装する必要があります。Listの実装クラスの例には
・ArrayList
・LinkedList
などがあります。一つ一つ見ていきましょう。

ArrayList

通常の配列は要素数が固定されるので「固定長配列」、ArrayList配列は要素数が変化するので「可変長配列」と呼ばれます。

無理にArrayListを使わずとも、通常の配列だけでプログラムを組むことは可能ですが、
不特定量のデータを扱う場合に配列の要素を見込みで大量に設定すなければならなくなり、
メモリを無駄に消費してしまう結果となってしまいます。
可変長配列であるArrayListを使えば、データ量に合わせ配列の大きさが変わるため、データ量を気にする必要や修正の必要もなくなります。

ArrayListの宣言

ArrayListの宣言時は以下の方法で実行可能です。

ArrayList<データ型名> オブジェクト変数名 = new ArrayList<扱うデータ型名>(初期サイズ値);

実際のコードとしては以下のようになります。

// 文字列のリスト
ArrayList<String> strList = new ArrayList<String>();

// 整数型のリスト
ArrayList<Integer> intList = new ArrayList<Integer>();

また、宣言時にファイル上部に以下の文章が追記されたかと思います。

import java.util.ArrayList;
import java.util.List;

ListやArrayListは「javaの標準ライブラリ」の中に入っている機能です。

プログラムを書くときに、Stringやint型、System等のような使用頻度が高いものは初めから使えるのですが、
それらと比べて使用頻度が低いものはJava実行時に読み込まれないようになっています。

そのようなライブラリは他にも多数あり、基本的には使わなければ、
その機能を読み込まなくなるため、プログラムの実行が軽くなるメリットが生まれます。

今回はそのライブラリを使用したいため、importでJava標準ライブラリの機能を使います、と宣言しています。

ArrayList配列への格納

次に作成された変数に中身を入れていきます。
Listに中身を入れていくには、ArrayList配列に提供されているデータ格納用メソッド「add()」を利用します。

オブジェクト変数名.add(データ);

実際のコードとしては以下のようになります。

// 文字列型を扱う可変長配列へ格納
strList.add("Hello");

// 整数型を扱う可変長配列へ格納
intList.add(100);
ArrayList配列から取得

ArrayList配列に格納されているデータを取得する際には以下の構文を使用します。

オブジェクト変数名.get(インデックス);

配列では配列変数を、System.out.println();でそのまま出力などすると、参照を表す文字列が表示されてしまいましたが、Listやこの後のMapの場合はその中身が出力されます

都度出力し、配列内に何が入っているかの確認に使用するようにしましょう。。

ArrayList配列に格納したデータサイズを確認

ArrayList配列に格納されているデータ数を数えたい際には以下の構文を使用します。

オブジェクト変数名.size();

lengthとの違いですが、sizeがListなどのコレクションの要素数を返すのに対し、lengthは配列の長さを返します。
ちなみにListが空の時は0が出力されます。

ArrayList配列に格納したデータを変更

ArrayList配列に格納されているデータを変更する際には変更用メソッド「set()」を使用します。

オブジェクト変数名.set(インデックス , データ);

インデックスで配列の中から何番目かを指定し、指定した中身に対しどのようなデータを入れるかを確定させることが出来ます。

ArrayList配列に格納されているデータを一つ削除

ArrayList配列に格納されているデータを削除する際には「remove()」を使用します。

オブジェクト変数名.remove(インデックス);

削除する場合、配列の中から何番目を削除したいのか指定し、削除します。
削除された後は、削除した配列より後の中身が繰り上げられます。

ArrayList配列に格納されたデータから検索

任意データ検索用メソッド「indexOf()」を利用して、格納されているデータの中から検索を行うことができます。

オブジェクト変数名.indexOf(検索データ);

第1引数で指定したデータを配列から検索し、見つかった場合はそのインデックスを、見つからなかった場合「-1」を戻り値として返します。

ArrayList配列に格納されている全データの削除

全削除用メソッド「clear()」を利用することで、配列の初期化が可能です。

オブジェクト変数名.clear();

ArrayList配列の格納データ数が空なのかを判定

空判定用メソッド「isEmpty()」を利用して、配列が空かどうか判定が可能です。

オブジェクト変数名.isEmpty();

ArrayList配列が空かどうかを判定し、空だった場合「true」を、そうでなかったら「false」を戻り値として返します。

これらのメソッドを用いて先程作成したArrayList配列を操作してみましょう

// データの追加
strList.add("こんばんは");
strList.add("おはよう");
strList.add("おやすみ");
strList.add("いただきます");
        
// データの取得
System.out.println(strList.get(0));
        
// データサイズを確認
int dataSize = strList.size();
System.out.println(dataSize);
        
// データの変更
strList.set(0, "こんにちは");
// 配列の中身を確認
System.out.println(strList);
        
// データの削除
strList.remove(2);
// 配列の中身を確認
System.out.println(strList);
        
// データの検索(該当した場合)
int search1 = strList.indexOf("こんにちは");
System.out.println(search1);
// データの検索(該当しなかった場合)
int search2 = strList.indexOf("ごちそうさまでした");
System.out.println(search2);
        
// データの全削除
strList.clear();
        
// データ数が空なのかを判定(空の場合)
boolean result1 = strList.isEmpty();
System.out.println(result1);
// データ数が空なのかを判定(空の場合)
boolean result2 = intList.isEmpty();
System.out.println(result2);

LinkedList

使い方はArrayListと大きく変わりませんが、処理時の動きが異なります。

LinkedListでは、それぞれの要素が前の要素と次の要素の情報を持ち、
先頭の要素から順番にたどっていくことで、各要素へアクセスします。

特定の要素へアクセスするスピードが速いArrayListに対し、
LinkedListは要素を追加したり、削除するスピードが早いという特徴があります。

要素数が多く、要素の挿入・削除を頻繁に行うことが予想される場合はLinkedList、
動的にサイズが拡張される配列」としてコレクションを使用したい場合はArrayListなど用途に応じてListインターフェースを使い分けましょう。

練習問題

「com.cmps.array」パッケージ内に「ArrayQuestion」クラスを作成してください。

問1: 日本の全種類の硬貨の金額が高い順に格納された配列を作成して二番目を表示してください。

問2: 適当なArrayList配列を用意して、要素数と内容を表示してください。

問3: [“田中”, “25”, “男性”]といった氏名・年齢・性別の3つの値が格納された配列 を3つ格納した多次元配列を作成し、for文を使用して全て表示してください。表示例:「田中:25歳男性」

練習問題のヒント
問1
答えを格納するための変数を用意し、その変数を出力します。

2
要素数を取得するには、size()を用います。

問3
最終的には、for文のネストを使用して作成します。

以下のLevelに沿って段階的に作成し、解答に近づけていきましょう。
【Level.0】まずはfor文を使わずに、配列の要素を取り出し、文字列と連結して出力する。
【Level.1】Level.0をfor文でループ処理にします。array[i][0]、array[i][1]、array[i][2]のように、配列の直接のインデックスにfor文のループ変数を入れます。
 Level.0では3行書いた連結文を、ループ処理で1行の記述にすることができます。
【Level.2】さらにfor文を使ってネストにし、array[i][j]のように、階層の深い方のインデックスもループ処理にします。i が外側のfor文、j が内側のfor文の変数です。
 *プログラムでは、繰り返し処理を用いて記述を短くできる部分は短くするのが鉄則。
 →文章を入れるための変数を用意し、取り出した値を変数に連結させていきます。(この時点では連結したものを表示すると”田中25男性”)
 →if文で条件分岐をして、文章に「:」や「歳」を付ける処理を記述します。
 →最終的に出来上がった文を出力します。

少し似た例題を以下に示すので参考にしてください。

// 以下の配列charactersを、「プーはテディベアです」の形式ですべて出力する。
String[][] characters = { { "プー", "テディベア" }, { "ピグレット", "子豚" }, { "ティガー", "トラ" } };

// 【Level+0】番号を指定して要素を取り出し、文字列と連結して出力
System.out.println(characters[0][0] + "は" + characters[0][1] + "です");
System.out.println(characters[1][0] + "は" + characters[1][1] + "です");
System.out.println(characters[2][0] + "は" + characters[2][1] + "です");

// 【Level+1】Level+0をループ処理で1行にする
for (int i = 0; i < 3; i++) {
    System.out.println(characters[i][0] + "は" + characters[i][1] + "です");
}

// 【Level+2】characters[i][x]という形式が複数回登場しているので共通化する
for (int i = 0; i < characters.length; i++) {
    String text = "";// 連結するために空文字列を用意
    for (int j = 0; j < characters[i].length; j++) {
        text = text + characters[i][j];
        if (j == 0) text = text + "は";
        if (j == 1) text = text + "です";
    }
    System.out.println(text);// 連結して出来上がった文を出力
}
タイトルとURLをコピーしました