5日目のゴール
5日目のテーマは
「ArrayList の“順番”を意識して扱えるようになること」 です。
ここまでであなたは、
ArrayList は伸び縮みする箱
クラスのフィールドとして ArrayList を持てる
オブジェクト(Task など)のリストを作れる
探す・更新する・削除する、といった操作ができる
というところまで来ています。
5日目ではここから一歩進んで、
ArrayList の「順番」が意味を持つこと
並び替え(ソート)という考え方
“同じ中身を持つ別のリスト”を作るときの注意点
を、小さな「本の並び替えアプリ」を題材にして固めていきます。
今日の題材:本のリストを「並び替え」できるようにする
まずはシンプルな Book クラス
本を表すクラスを用意します。
public class Book {
String title;
int price;
Book(String title, int price) {
if (title == null || title.isEmpty()) {
throw new IllegalArgumentException("タイトルは必須です。");
}
if (price < 0) {
throw new IllegalArgumentException("価格は 0 以上で指定してください。");
}
this.title = title;
this.price = price;
}
void show() {
System.out.println("タイトル: " + title + " / 価格: " + price + "円");
}
}
Javaここでは「タイトル」と「価格」だけに絞っています。
1冊分の情報をまとめて持つ、というこれまでの流れの延長です。
本のリストを管理する BookList クラス
import java.util.ArrayList;
public class BookList {
ArrayList<Book> books;
BookList() {
books = new ArrayList<>();
}
void add(Book book) {
if (book == null) {
System.out.println("null の本は追加できません。");
return;
}
books.add(book);
}
void showAll() {
System.out.println("=== 本一覧 ===");
if (books.isEmpty()) {
System.out.println("本はまだありません。");
return;
}
for (int i = 0; i < books.size(); i++) {
System.out.print((i + 1) + "冊目: ");
books.get(i).show();
}
}
}
Javaここまでは、これまでと同じ「可変長データをクラスの中に閉じ込める」パターンです。
ここから、この books の「順番」を意識して扱っていきます。
ArrayList の「順番」は意味を持つ
順番が変わると“見え方”が変わる
次のように本を追加してみます。
public class Main {
public static void main(String[] args) {
BookList list = new BookList();
list.add(new Book("Java入門", 3000));
list.add(new Book("アルゴリズム図鑑", 2500));
list.add(new Book("オブジェクト指向設計", 4000));
list.showAll();
}
}
Javaこのときの表示は、追加した順番になります。
1冊目: Java入門
2冊目: アルゴリズム図鑑
3冊目: オブジェクト指向設計
でも、現実のアプリでは、
価格の安い順に並べたい
タイトルの五十音順に並べたい
といった「並び替え」がしたくなります。
ここで初めて、
「ArrayList の順番を意図的に変える」
という操作が必要になります。
価格の昇順に並び替える(自前ソート)
まずは「一番安い本」を見つけるイメージ
いきなり全部を並び替えるのではなく、
まずは「一番安い本を見つける」ことから考えます。
Book findCheapest() {
if (books.isEmpty()) {
return null;
}
Book cheapest = books.get(0);
for (int i = 1; i < books.size(); i++) {
Book current = books.get(i);
if (current.price < cheapest.price) {
cheapest = current;
}
}
return cheapest;
}
Javaここでやっていることは、とてもシンプルです。
最初に「仮の最安」を 0 番目にしておく
1番目以降を順番に見ていき、もっと安い本があれば更新する
最後に「一番安かった本」を返す
これは「最小値を探す」という基本パターンです。
この考え方を、並び替えに発展させます。
選択ソートという考え方
「一番安い本を先頭に持ってくる」
「残りの中で一番安い本を2番目に持ってくる」
…というのを繰り返すと、
価格の昇順に並び替えられます。
これをコードにすると、こうなります。
void sortByPriceAsc() {
for (int i = 0; i < books.size() - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < books.size(); j++) {
if (books.get(j).price < books.get(minIndex).price) {
minIndex = j;
}
}
if (minIndex != i) {
Book tmp = books.get(i);
books.set(i, books.get(minIndex));
books.set(minIndex, tmp);
}
}
}
Java少し長いので、流れを言葉で追ってみます。
外側の for(i)は「確定させる位置」を表す
i 番目に「その位置に来るべき最安の本」を置きたい
内側の for(j)は「i 以降の中で最安の本の位置」を探すminIndex に「最安の本のインデックス」を覚えておく
見つかった最安の本と、i 番目の本を入れ替える
ここで新しく出てきた set も重要です。
books.set(i, books.get(minIndex));
Javaset(index, value) は、
「index 番目の要素を value に置き換える」メソッドです。
add は「間に割り込んで押し出す」set は「その場所の中身だけを上書きする」
という違いがあります。
並び替え前後を Main から確認する
実行イメージをつかむ
BookList に sortByPriceAsc を追加したとします。
public class Main {
public static void main(String[] args) {
BookList list = new BookList();
list.add(new Book("Java入門", 3000));
list.add(new Book("アルゴリズム図鑑", 2500));
list.add(new Book("オブジェクト指向設計", 4000));
System.out.println("=== 並び替え前 ===");
list.showAll();
list.sortByPriceAsc();
System.out.println("=== 価格の安い順に並び替えた後 ===");
list.showAll();
}
}
Java表示はこういうイメージになります。
並び替え前
1冊目: Java入門(3000)
2冊目: アルゴリズム図鑑(2500)
3冊目: オブジェクト指向設計(4000)
並び替え後
1冊目: アルゴリズム図鑑(2500)
2冊目: Java入門(3000)
3冊目: オブジェクト指向設計(4000)
ここで感じてほしいのは、
ArrayList の「中身の順番」を自分でコントロールできる
順番が変わると、アプリの“見え方”も変わる
ということです。
「別のリストにコピーする」ときの落とし穴
代入は“同じリストを指すだけ”
次のコードを見てください。
ArrayList<Book> a = new ArrayList<>();
a.add(new Book("Java入門", 3000));
ArrayList<Book> b = a;
b.add(new Book("アルゴリズム図鑑", 2500));
System.out.println(a.size()); // いくつ?
System.out.println(b.size()); // いくつ?
Java直感的には「a と b は別物だから、サイズも違うはず」と思いがちですが、
実際にはどちらも 2 になります。
理由はシンプルで、
ArrayList<Book> b = a; は
「a と同じリストを b という別名で呼んでいるだけ」
だからです。
a と b は「別の箱」ではなく、
同じ ArrayList オブジェクトを指す“2つの変数”になっています。
本当に“別のリスト”が欲しいとき
「a の中身をコピーして、b は b で自由に並び替えたい」
というときは、こう書きます。
ArrayList<Book> a = new ArrayList<>();
a.add(new Book("Java入門", 3000));
a.add(new Book("アルゴリズム図鑑", 2500));
ArrayList<Book> b = new ArrayList<>(a);
Javanew ArrayList<>(a) は、
「a と同じ要素を持つ新しい ArrayList」を作ります。
ここで大事なのは、
リストそのものは別物になる
でも、中に入っている Book オブジェクトは同じ参照
という点です。
つまり、
b 側で b.set(0, new Book(...)) のように「要素そのもの」を差し替える
→ a には影響しない
b 側で b.get(0).price = 1000; のように「Book の中身」を変える
→ a 側から見える Book も同じように変わる
という動きになります。
ここで感じてほしいのは、
ArrayList 自体と、その中に入っているオブジェクト
この2つを頭の中で分けて考える必要がある
ということです。
5日目のミニアプリ完成形
BookList に並び替えとコピーを追加した形
import java.util.ArrayList;
public class BookList {
ArrayList<Book> books;
BookList() {
books = new ArrayList<>();
}
BookList(ArrayList<Book> source) {
books = new ArrayList<>(source);
}
void add(Book book) {
if (book == null) {
System.out.println("null の本は追加できません。");
return;
}
books.add(book);
}
void showAll() {
System.out.println("=== 本一覧 ===");
if (books.isEmpty()) {
System.out.println("本はまだありません。");
return;
}
for (int i = 0; i < books.size(); i++) {
System.out.print((i + 1) + "冊目: ");
books.get(i).show();
}
}
void sortByPriceAsc() {
for (int i = 0; i < books.size() - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < books.size(); j++) {
if (books.get(j).price < books.get(minIndex).price) {
minIndex = j;
}
}
if (minIndex != i) {
Book tmp = books.get(i);
books.set(i, books.get(minIndex));
books.set(minIndex, tmp);
}
}
}
}
JavaMain で「元の順番」と「並び替え後」を両方見る
public class Main {
public static void main(String[] args) {
BookList original = new BookList();
original.add(new Book("Java入門", 3000));
original.add(new Book("アルゴリズム図鑑", 2500));
original.add(new Book("オブジェクト指向設計", 4000));
System.out.println("=== 元の順番 ===");
original.showAll();
BookList sorted = new BookList(original.books);
sorted.sortByPriceAsc();
System.out.println("=== 価格の安い順(別リスト) ===");
sorted.showAll();
System.out.println("=== 元の順番は変わっていないか? ===");
original.showAll();
}
}
Javaここで確認してほしいのは、
original と sorted は別の BookListsorted の中の ArrayList は、original.books をコピーして作っているsorted を並び替えても、original の順番は変わらない
という動きです。
5日目で絶対に押さえてほしい本質
今日いちばん大事なのは、
「ArrayList の“順番”と、“中身のオブジェクト”を分けて考えられるようになったかどうか」 です。
ArrayList の順番は、アプリの見え方に直結するset を使うと「その場所の中身だけ」を入れ替えられる
自前でソートを書くと、「最小値を探す」「入れ替える」という基本パターンが見えてくるnew ArrayList<>(元リスト) で「同じ要素を持つ別リスト」を作れる
ただし、中のオブジェクトは同じ参照なので、「中身を書き換える」と両方に影響する
この感覚が入っていれば、
6日目以降にやる「標準ライブラリの sort を使う」「Comparator で並び替え条件を変える」
といった、より実践的なテクニックもスッと入ってくるようになります。

