Skip to content

Java講座: 配列

配列とは?

配列を一言で説明すると「変数をまとめた箱のようなもの」だと思っています。(筆者の主観混じり)
例えば、生徒が30人いるクラスのテストの点数を記録したいとします。

java
int score1 = 80;
int score2 = 90;
int score3 = 75;
...

こんな感じのプログラムを書いていたらどうでしょうか?一見するとデータを纏めて管理しているようにも見えますが、score1score2...はそれぞれなんの関係もない独立した変数であり、今何人分の点数を登録したかをすぐにカウントすることはできませんし、すべての点数を出力したくても変数を個々に出力するしか方法がありません。端的に言うと不便です。

そこで役立つのが配列です。Javaでの配列の書き方を解説します。
生徒が30人いるクラスのテストの点数を記録したいときのプログラムを配列を使って書いたときのプログラムです。一つずつ解説します。

java
public class Main {
    public static void main(String[] args) {
        int[] scores = new int[30];

        scores[0] = 80;
        scores[1] = 90;
        scores[2] = 75;
        // ...
    }
}

Javaでは、配列を以下のように宣言します。

java
int[] scores;

しかし、これではまだ宣言しただけなので使えません。配列を使用するためには、配列のサイズを指定して初期化する必要があります。

java
int[] scores = new int[30]; // サイズ30の配列を初期化

今回はサイズを30で指定したため、この配列には30個のデータを格納することができます。

配列の各要素にアクセス・操作するためには、scores[0]のように変数名[番地]の形式で記述します。サンプルコードの

java
scores[0] = 90;
scores[1] = 80;
scores[2] = 75;

というコードは、「配列の先頭に90を代入、その次に80を代入⋯」といった処理を行っているわけですね。

ここで注意点なのですが、配列の番地は0からスタートします。scoresの1番目の要素にアクセスしたい場合はscores[0]と記述しなければいけないことに気をつけてください。

では、番地が0からスタートするということは、この配列の最後の番地は何番目でしょう?
考えてみればすぐわかりますが、初心者のうちはいざコードを書くとなるとうっかりscores[30]にアクセスしようとしがちです。これは配列外参照というエラーになるので気をつけましょう。

今回実装した配列を視覚的に表現するとこんな感じです。理解の参考程度に。

1次元配列

配列とfor文

配列の強みはなんといってもfor文との相性が抜群に良いことです。
例えば、num[]というint型の配列(サイズは10とします)に入れる要素を標準入力で決定し、その総和を出力したいとき、このようなコードを書けばスマートに書けます。

java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int[] num = new int[10];

        int sum = 0;

        for (int i = 0; i < 10; i++) {
            int x = sc.nextInt();

            num[i] = x;
            System.out.println("numの" + i + "番地の値は " + num[i] + " です");
            sum += num[i];
        }

        System.out.println("総和: " + sum);
    }
}

実行例:

java Main
1
numの0番地の値は...
2
numの1番地の値は...
...
総和: 15

では実際に配列を使った練習問題を解いてみましょう。

練習問題


https://atcoder.jp/contests/abc290/tasks/abc290_a
https://atcoder.jp/contests/arc025/tasks/arc025_1
https://atcoder.jp/contests/abc205/tasks/abc205_b

2次元配列

今回説明した配列は、厳密には1次元配列といいます。ということは、当然2次元配列もありますね。

1次元配列がリストのようなイメージのものだとしたら、2次元配列はのようなものです。視覚化すると以下のようになります。

2次元配列

この2次元配列は3行4列ですね。これをJavaで初期化するには以下のように書きます。

java
String[][] s = new String[3][4];

2次元配列の各要素にアクセスするには、その要素が何行目の何列目かを指定する必要があります。たとえば、このsという配列の1行目の3列目をcとしたい場合は、以下のように書きます。

java
s[0][2] = "c";

2次元配列でも、番地は0から始まることに注意してください。

余談

配列の初期化方法

基本的には配列はnew int[(サイズ)]といったようなサイズだけを宣言して初期化することが多いですが、以下のような初期化も可能です。

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

このように書くことで、配列のサイズとその要素の具体的な値をまとめて初期化することができます。このとき、配列のサイズは初期化時の要素数で固定されます。このプログラムではaのサイズは5になるわけです。

配列の長さの取得

配列の長さは配列名.lengthで取得することができます。

java
String[] s = new String[10];
System.out.println(s.length); // 出力: 10

for文で配列の要素をすべて取得するときに便利です。

java
int[] a = new int[100];

for (int i = 0; i < a.length; i++) {
    System.out.println(a[i]);
}

配列のソート

ソートとは、配列内の要素を小さい順に並べることです。 [4, 2, 3, 5, 1]みたいな配列を[1, 2, 3, 4, 5]みたいに並べ替えたいときには、Arrays.sort()メソッドが利用できます。

java
import java.util.Arrays; // 絶対書く!

public class Main {
    public static void main(String[] args) {
        int[] a = {4, 2, 3, 5, 1};
        Arrays.sort(a);
        
        System.out.println(Arrays.toString(a));
    }
}

Arrays.toString()メソッドを使うことで、配列を見やすく出力することができます。 一方で、Javaの配列を降順(大きい順)にソートするのは非常にめんどくさいです。Collections.reverseOrder()sortメソッドの第二引数に設定することでできますが、そのためにはaint型の配列ではなくInteger型の配列にしなければいけません。

拡張for文

配列の中身の情報だけが必要で、インデックス(配列の番地のこと)がいらないとき、拡張for文が非常に役に立ちます。
拡張for文の構文は以下のとおりです。

java
for (型名 変数名 : 配列名) {
    
}

サンプルコードも示します。

java
int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int sum = 0;
for (int num : numbers) {
    System.out.println("num = " + num);
    
    sum += num;
}

System.out.println("sum = " + sum);

インポートめんどくせえ

java.util.Scannerもインポートしなきゃだし、java.util.Arraysもインポートしなきゃだしでめんどくせえと思った人がいるかも知れません。開発時にはできる限り何をインポートしているのかわかるようにしたほうが好ましいのですが、めんどくさいもんはめんどくさいのでサボる方法を教えます。

java
import java.util.*;

これでjava.utilパッケージのすべてのクラスをインポートできます。(厳密にはすべてを一気に読み込んでいるわけではないが)
java.ioパッケージをインポートしたい場合にはjava.io.*とか書けばできます。このようなインポート方法をオンデマンド型インポートといいます。

さらに、Java25からは最強のインポート宣言ができるようになりました。

java
import module java.base;

これだけでJavaがサポートしているパッケージをすべてインポートすることができます。C++の#include <bits/stdc++.h>になんか似てますね。
しかし、このインポート方法はごくまれに複数パッケージ間の衝突が発生します。(以下のコードはOracle公式からの引用です)

java
import java.awt.Frame;
import java.awt.Label;
import java.awt.List;
import java.awt.event.WindowAdapter;   
import java.awt.event.WindowEvent;    
import java.util.Arrays;

public class FruitApp { 
      
    FruitApp(java.util.List<String> fruits) {  
        Frame f = new Frame();  
        Label l = new Label("Fruits");   
        List lst = new List();
        fruits.forEach(i -> lst.add(i));
        l.setBounds(20, 40, 80, 30);  
        lst.setBounds(20, 70, 80, 80);  
        f.add(l);
        f.add(lst);  
        f.setSize(200,200);  
        f.setTitle("Fruit");   
        f.setLayout(null);   
        f.setVisible(true);  

        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                System.exit(0);
            }
        });      
    }    
      
    public static void main(String args[]) {   
        String[] fruits = new String[] { "apple", "berry", "citrus" };
        FruitApp f = new FruitApp(Arrays.asList(fruits));    
    }  
}

このプログラムではjava.awt.Listjava.util.Listを使用していますが、もしこのプログラムのインポートをimport module java.baseでサボると、マシンはどっちのListを使えば良いのかわからなくなり、エラーになります。
解決方法は公式サイト見てください。(書くのめんどい)