配列はサイズが固定されているため、拡張が必要な場合はコレクションフレームワークを使用するのが一般的である。
コレクションフレームワークの概要(java.utilパッケージ)
java.utilコレクションは 크게2つのカテゴリに分類される:
- Collection:単一の要素(value)を格納
- Map:キーと値のペア(key-value)を格納
Collectionはさらに細分化される:
- List:順序付けられ、重複を許容する
- Set:順序付けられず、重複を許容しない
Listインタフェース主要な実装クラス:
- ArrayList:検索処理が高速
- Vector:同期化を지원(ArrayListの旧版)
- Stack:後入れ先出し(LIFO)構造
- LinkedList:挿入・削除処理が高速
ArrayListの操作
package com.example.collections;
import java.util.ArrayList;
import java.util.List;
public class ArrayListSample {
public static void main(String[] args) {
// コンストラクタのバリエーション
List<String> list1 = new ArrayList<>(); // 初期容量指定なし
List<String> list2 = new ArrayList<>(10); // 初期容量10
List<String> list3 = new ArrayList<>(list2); // 既存コレクションから生成
List<Integer> list4 = new ArrayList<>(); // ジェネリック тип指定
// 要素の追加
list4.add(100);
list4.add(200);
list4.add(300);
displayElements(list4);
// 100 200 300
list4.add(1, 150); // インデックス1に挿入
displayElements(list4);
// 100 150 200 300
// 要素の削除
list4.remove(1); // インデックス1の要素を削除
displayElements(list4);
// 100 200 300
list4.remove(Integer.valueOf(200)); // 値を指定して削除
// 要素の変更
list4.set(0, 500); // インデックス0の要素を置換
displayElements(list4);
// 500 300
// 要素の取得
System.out.println("インデックス1の要素: " + list4.get(1));
// インデックス1の要素: 300
// サイズの取得
System.out.println("コレクションサイズ: " + list4.size());
// コレクションサイズ: 2
// 全要素の表示(toStringメソッドがオーバーライド済み)
System.out.println(list4);
// [500, 300]
// 他のコレクション操作
List<String> baseList = new ArrayList<>();
baseList.add("X");
baseList.add("Y");
baseList.add("Z");
List<String> targetList = new ArrayList<>();
targetList.add("A");
targetList.addAll(baseList); // 全要素を追加
System.out.println(targetList);
// [A, X, Y, Z]
targetList.retainAll(baseList); // 共通要素のみ残す(交集)
System.out.println(targetList);
// [X, Y, Z]
targetList.clear(); // 全要素を削除
System.out.println(targetList.isEmpty());
// true
// 要素の存在確認
List<String> searchList = new ArrayList<>();
searchList.add("P");
searchList.add("Q");
searchList.add("P");
searchList.add("R");
System.out.println("Pが含まれるか: " + searchList.contains("P"));
// Pが含まれるか: true
System.out.println("最初のPのインデックス: " + searchList.indexOf("P"));
// 最初のPのインデックス: 0
System.out.println("最後のPのインデックス: " + searchList.lastIndexOf("P"));
// 最後のPのインデックス: 2
// 配列への変換
String[] stringArray = searchList.toArray(new String[0]);
for (String item : stringArray) {
System.out.print(item + " ");
}
System.out.println();
// P Q P R
}
private static void displayElements(List<Integer> list) {
for (Integer num : list) {
System.out.print(num + " ");
}
System.out.println();
}
}
VectorとStackの特徴
Vectorは旧型のコレクションクラスであり、スレッドセーフ(同期化)が保証される。シングルスレッド環境ではArrayListの方が高速である。
Vectorのデフォルト拡張率は2倍。ArrayListは1.5倍(capacity + (capacity >> 1))である。
Stackクラス(後入れ先出し):
package com.example.collections;
import java.util.Stack;
public class StackSample {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
// 要素をスタックに押し込む
stack.push("First");
stack.push("Second");
stack.push("Third");
System.out.println("pop実行: " + stack.pop()); // トップ要素を取り出す
// pop実行: Third
System.out.println("peek実行: " + stack.peek()); // トップ要素を Peak(削除しない)
// peek実行: Second
System.out.println("現在のスタック: " + stack);
// 現在のスタック: [First, Second]
System.out.println("'First'の位置: " + stack.search("First")); // 栈頂から数えた位置
// 'First'の位置: 2
}
}
LinkedListとQueue(先入れ先出し)
LinkedListはQueueインタフェースも実装しており、両方の特性を持つ。
package com.example.collections;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
public class LinkedListSample {
public static void main(String[] args) {
// Queueとしての使用(先入れ先出し)
Queue<String> queue = new LinkedList<>();
queue.offer("Item1");
queue.offer("Item2");
queue.offer("Item3");
System.out.println("poll: " + queue.poll()); // 要素を取り出して削除
// poll: Item1
System.out.println("peek: " + queue.peek()); // 先頭を確認(削除しない)
// peek: Item2
// LinkedListのメソッド利用
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.add("C");
linkedList.addFirst("Start"); // 先頭に挿入
linkedList.addLast("End"); // 末尾に追加
System.out.println(linkedList);
// [Start, A, B, C, End]
// Stackからの変換
Stack<String> stack = new Stack<>();
stack.push("X");
stack.push("Y");
stack.push("Z");
LinkedList<String> fromStack = new LinkedList<>(stack);
System.out.println(fromStack);
// [X, Y, Z]
// 要素の移除(先入れ先출력なので先頭から)
System.out.println("remove: " + linkedList.remove());
// remove: Start
System.out.println(linkedList);
// [A, B, C, End]
// 性能比較:100,000件の要素を先頭に挿入
LinkedList<String> linked = new LinkedList<>();
ArrayList<String> array = new ArrayList<>();
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
linked.add(0, "Data");
}
long endTime1 = System.currentTimeMillis();
long startTime2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
array.add(0, "Data");
}
long endTime2 = System.currentTimeMillis();
System.out.println("LinkedList所要時間: " + (endTime1 - startTime1) + "ms");
System.out.println("ArrayList所要時間: " + (endTime2 - startTime2) + "ms");
// LinkedListは先頭への挿入が高速
// ArrayListは末尾への挿入場合は高速
}
}