配列; Arrays¶
配列のメリット¶
たとえば、英語・数学・国語・理科・社会の5教科の点数を管理するプログラムを作ろうとするとします。
配列を使わずにそれぞれの点数を変数で管理しようとすると、次のように一つひとつ別の名前をつけて変数を定義することになります。
public class ScoreExample {
public static void main(String[] args) {
int score1 = 80;
int score2 = 75;
int score3 = 90;
int score4 = 85;
int score5 = 70;
int total = score1 + score2 + score3 + score4 + score5;
double average = total / 5.0;
System.out.println("合計点: " + total);
System.out.println("平均点: " + average);
}
}
このように変数を個別に用意する方法には、いくつかの問題があります。
-
- 変数が増えるとコードが複雑になる
- 教科が増えるたびに、新しい変数を用意しなければなりません。同じようなデータが並ぶだけなのに、名前を毎回考えて記述するのは手間がかかります。
-
- 処理が繰り返しになりやすい
- 平均点を計算したいときなどに、すべての変数を一つずつ足し合わせる必要があり、同じような処理を何度も書くことになります。
-
- 柔軟性が低い
- もし教科の数が変更されたり、別の用途で同様の構造のデータを扱う必要が出てきたとき、コードをすべて書き換える必要があります。これはメンテナンス性が悪く、ミスが起きやすくなります。
こうした課題を解決するために登場するのが配列です。配列を使えば、同じ種類のデータを「まとめてひとつの変数」として扱えるようになり、コードが簡潔で柔軟になります。
配列でこの課題を解決する
先ほどのように、5教科の点数を管理する場合、配列を使えば次のように記述できます。
int[] scores = {80, 75, 90, 85, 70};
このように、同じ型のデータをまとめてひとつに管理できるのが配列の特徴です。これによって次のようなメリットがあります。
- 教科の数が増減しても、変数名をたくさん用意する必要がありません。
- すべての教科の合計や平均を求める処理を、繰り返し(ループ)を使って簡潔に書くことができます。
配列の「要素」と「添え字」¶
配列には複数のデータが順番に並んで格納されています。このひとつひとつのデータのことを要素(element)と呼びます。
それぞれの要素には番号が振られており、その番号を使ってアクセスします。この番号のことを添え字(index)と言います。
Javaでは、添え字は0から始まるのが特徴です。
インデックス | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
値(スコア) | 80 | 75 | 90 | 85 | 70 |
たとえば、3番目の教科の点数を使いたい場合は、次のように書きます。
System.out.println(scores[2]); // 結果: 90
このように、配列を使えば、データの管理・処理がずっと効率的になります。
配列の扱い方¶
■ 配列の作成(宣言と代入)
int[] scores = new int[5]; // 5つのint型の要素を持つ配列
※ 要素の中身は、初期化しない限り 0 が自動で入る(基本型の場合)。
■ 配列の利用(代入と参照)
scores[0] = 70;
System.out.println(scores[0]); // 70 と出力される
■ 配列の初期化(同時に値を代入)
int[] scores = {70, 80, 90, 85, 75};
■ インデックス(添え字)の注意点
- 配列のインデックスは 0からスタート。
- 範囲外のインデックスにアクセスすると、ArrayIndexOutOfBoundsException になる。
System.out.println(scores[5]); // エラー:5番目は存在しない
■ for文との組み合わせ
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
scores.length
で配列の長さを取得。- 添え字 i を使って要素を順番に処理。
📘 配列とfor文の組み合わせ¶
配列は、同じ型のデータをまとめて管理するための箱でしたね。 でも、それぞれの要素にアクセスしたり、まとめて処理したりするには、繰り返し処理が必要になります。
そこで登場するのが、for文との組み合わせです。
✅ 例:5科目の点数の合計を計算する
public class Main {
public static void main(String[] args) {
int[] scores = {70, 80, 65, 90, 75}; // 英数国理社の点数
int sum = 0;
for (int i = 0; i < scores.length; i++) {
sum += scores[i]; // i番目の点数を足していく
}
System.out.println("合計点は " + sum + " 点です");
}
}
scores.length
で、配列の要素数。5
が返るので、i = 0
からi < 5
まで繰り返します。for (int i = 0; i < scores.length; i++)
のように、「length」 を使えば、要素数が変わっても対応可能です。scores[i]
で、i番目の要素にアクセスします(0番目が最初)。sum += scores[i];
で、毎回のループで点数を加算します。
💡 この構文を使うと便利な場面:
- 合計点・平均点の計算
- 条件に合うデータの抽出
- 全体に同じ処理を適用(例:10点加点など)
🔁 拡張for文(for-each文)の使い方¶
Javaでは、配列やリストなどを1つずつ順番に処理したい場合、 通常のfor文よりも シンプルに書ける構文 があります。 それが、拡張for文(for-each文)です。
for (型 変数名 : 配列名) {
// 各要素に対しての処理
}
:
は「in」のような意味と捉えるとわかりやすいです。- 各要素が順番に取り出されて、変数に入ります。
✅ 実例:5科目の点数を出力する:
public class Main {
public static void main(String[] args) {
int[] scores = {70, 80, 65, 90, 75};
for (int score : scores) {
System.out.println("点数:" + score);
}
}
}
注意点
拡張for文では 要素のインデックス番号(0, 1, 2…)は使えません。 インデックスを使いたい場合は、通常のfor文を使いましょう。
🔬 配列とfor文を使って塩基記号を表示するプログラム¶
サイズ10の配列に 1〜4 のランダムな整数を格納し、それぞれに対応する塩基記号(A, T, G, C)を表示するプログラムを、switch文を使って記述します。
例1:switch文で塩基記号を対応させる
import java.util.Random;
public class Main {
public static void main(String[] args) {
int[] bases = new int[10];
Random rand = new Random();
for (int i = 0; i < bases.length; i++) {
bases[i] = rand.nextInt(4) + 1;
}
for (int i = 0; i < bases.length; i++) {
char baseChar = switch (bases[i]) {
case 1 -> {
yield 'A';
}
case 2 -> {
yield 'T';
}
case 3 -> {
yield 'G';
}
case 4 -> {
yield 'C';
}
default -> {
yield '?';
}
};
System.out.println(baseChar);
}
}
}
1〜4の数値に応じて、塩基「A」「T」「G」「C」を対応させています。JavaではJava 14以降で導入された「switch式(switch expressions)」において、yield を使うことができます。
->
の形式で式として使えるswitch。- 複数行ブロックにしたいときは
{}
を使い、その中で値を返すにはyield
が必要。
🔁 より簡潔に書く方法:配列を使って直接変換する
この処理は、塩基記号を配列として持っておくことで、インデックスを使ってシンプルに記述できます。
例2:配列で塩基記号を直接参照する方法
import java.util.Random;
public class BaseExampleArray {
public static void main(String[] args) {
int[] bases = new int[10];
Random rand = new Random();
char[] baseSymbols = {' ', 'A', 'T', 'G', 'C'}; // 0番は使わない
// 配列に1〜4のランダムな数値を格納
for (int i = 0; i < bases.length; i++) {
bases[i] = rand.nextInt(4) + 1;
}
// 数値をインデックスとして塩基を取得・出力
for (int i = 0; i < bases.length; i++) {
System.out.print(baseSymbols[bases[i]] + " ");
}
}
}
✅ 解説:
baseSymbols[bases[i]]
という一文で塩基記号が取り出せるので、switch文よりも可読性・保守性に優れています。- 0番目に空白を入れることで、1〜4をそのままインデックスとして使えるよう工夫しています。
- この方法は「マッピング(対応関係)の明示」にもなっており、他のケースにも応用が可能です。
🧠 Javaにおける配列とメモリの仕組み¶
💡 配列は「参照型(Reference Type)」
Javaでは、配列は参照型に分類されます。つまり、配列変数は配列そのものではなく、配列のデータが格納されているメモリ領域への参照(アドレス)を保持しています。
🔍 具体的なイメージ
int[] numbers = new int[5];
このとき、Javaは以下のような処理を行います:
- int型のデータを5個格納できるメモリ領域を確保
- そのメモリ領域の先頭アドレスを、numbersという変数に保持
イメージとしてはこんな構造です:
numbers ──┐
▼
[ 0, 0, 0, 0, 0 ] ← 実体(メモリ上の配列)
このように、numbersには実際の配列データではなく、配列のアドレスが入っているのです。
📌 配列を別の変数に代入した場合
int[] other = numbers;
numbers
と other
は同じ配列を指すことになります。つまり、どちらかの変数を使って配列の要素を変更すれば、もう一方にも反映されます。
other[0] = 100;
System.out.println(numbers[0]); // → 100
- otherに新しい配列をコピーしているわけではなく、同じ配列のアドレスを渡しているだけ。
- この性質を知らないと、「意図しない値の変更」が起こることがあります。
❗ 値型との違いに注意
たとえば、以下のようなint(プリミティブ型)の代入では、値そのものがコピーされます。
int a = 10;
int b = a;
b = 20;
System.out.println(a); // → 10(変化なし)
🧭 まとめ
特徴 | 値型 (int , double など) |
参照型(配列など) |
---|---|---|
代入時の動作 | 値そのものがコピーされる | アドレス(参照)がコピーされる |
メモリの格納場所 | スタック | ヒープに実体、スタックに参照 |
変更の影響範囲 | 代入先のみ | すべての参照元に影響あり |
== の比較結果 |
値を比較する | アドレスを比較する |
null を代入可能か |
不可 | 可 |
Java のガーベージコレクション¶
ガーベージコレクションとは?
Javaでは、new
キーワードを使ってオブジェクトを作成すると、そのオブジェクトはヒープと呼ばれるメモリ領域に配置されます。しかし、プログラムが進むにつれて、使わなくなったオブジェクトもメモリ上に残り続けてしまうと、メモリが不足してしまいます。
そこで、Javaでは使われなくなったオブジェクト(=誰からも参照されていないオブジェクト)を自動的に削除する機能が用意されています。これが「ガーベージコレクション」です。
どのように動くの?
Javaのガーベージコレクタは、次のようなタイミングで動作します。
- メモリが不足しそうになったとき
- 明示的に
System.gc()
を呼び出したとき(ただし、実行される保証はありません)
ガーベージコレクタは、現在使われていないオブジェクトを探索し、それらを削除してメモリを回収します。これによって、プログラマはメモリ解放の処理を記述する必要がなくなり、安全かつ効率的にプログラムを開発できるのです。
「使われていないオブジェクト」とは?
以下のような状態になると、オブジェクトは「不要」とみなされます:
MyObject obj = new MyObject(); // オブジェクトを生成
obj = null; // 参照を切る → この時点でガーベージコレクションの対象になる
参照が切れたり、スコープの外に出てアクセスできなくなった場合、そのオブジェクトは「誰からも使われていない」と判断され、削除の候補になります。
メリット | 注意点 |
---|---|
メモリ管理を自動化できる | 実行タイミングは制御できない |
メモリリークのリスクを減らせる | 処理が重くなることがあり、動作が一時止まることも |
コードがシンプルに保てる | リアルタイム性が求められる処理には不向き |
まとめ
Javaのガーベージコレクションは、プログラム中で不要になったオブジェクトを自動的に削除してくれる仕組みで、Javaが「メモリ管理がしやすい言語」と言われる理由の一つです。ガーベージコレクションのおかげで、開発者はメモリ解放のことをそれほど意識せずに、より本質的なロジックに集中することができます。
多次元配列¶
多次元配列とは?
Javaでは、配列の中に配列を格納することで多次元配列を表現します。特によく使われるのが「2次元配列」です。これは、表(表形式のデータ)やマス目のようなデータを表すときに便利です。
2次元配列の宣言と初期化:
int[][] matrix = new int[3][4];
この例では、3行4列の整数の表(3×4の2次元配列)を作成しています。
matrix ──▶ [ [0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0] ]
- 行数:3
- 列数:4
- 添え字でアクセス:matrix[0][1] は1行目の2列目の要素
要素へのアクセス・変更:
matrix[1][2] = 99;
System.out.println(matrix[1][2]); // 出力:99
配列の初期化時に直接値を入れる方法:
int[][] scores = {
{70, 80, 90},
{60, 75, 85}
};
これは、2行3列の2次元配列で、初期値があらかじめ与えられています。
for文による走査(ネスト構造):
for (int i = 0; i < scores.length; i++) {
for (int j = 0; j < scores[i].length; j++) {
System.out.print(scores[i][j] + " ");
}
System.out.println();
}
- 外側のiが行
- 内側のjが列
scores.length
は行数scores[i].length
はその行の列数
不規則な配列(ジャグ配列)にも対応
Javaの2次元配列は「配列の配列」なので、各行の長さが異なる「ジャグ配列」も作れます:
int[][] jagged = {
{1, 2},
{3, 4, 5},
{6}
};
これは、各行の要素数が異なる特殊な形の配列です。
まとめ:2次元配列の特徴
特徴 | 内容 |
---|---|
構造 | 配列の中に配列(=行ごとの配列)で構成される |
用途 | 表形式のデータ、行列、マス目状のデータなどに便利 |
走査方法 | 二重の for 文(ネスト構造)を用いて各要素にアクセス |
初期化 | { {1,2}, {3,4,5}, {6} } のように異なる長さの配列も定義可能 |
柔軟性 | 各行の列数が異なる「ジャグ配列(Jagged Array)」も作成できる |