Javaには「オブジェクト指向」「例外処理」「マルチスレッド」など数多くの概念や仕組みがあります。その中でも日常的に使う機能の1つが、複数のデータをまとめて管理するための仕組み、コレクションフレームワークです。
プログラムを作っていると、以下のような場面に遭遇することが多いでしょう。
こうした「データの集まり」を扱うとき、配列(Array)を使うやり方ももちろんあります。しかし、配列には「後からサイズを変えにくい」「特定の要素を検索しづらい」などの弱点があります。これらの課題をよりスムーズに解決してくれるのが、Javaのコレクションフレームワークです。

コレクションフレームワークを使うと、要素の追加・削除、ソートや検索などを効率的かつ直感的に行えるようになります。また、同じインターフェースを実装したクラス同士であれば、実装クラス(ArrayListやHashSetなど)を差し替えるだけで、裏側の処理をガラリと変えることが可能です。そのため、保守性や再利用性が非常に高いのも魅力です。
コレクションフレームワークとは
コレクションフレームワークとは、超簡潔に説明すると「複数のデータを扱うための便利なクラスやインターフェースを、まとめて使いやすく整理したもの」です。もう少し平たく言うと、
イメージとしては、
- パッケージ(java.util)
- インターフェース(List、Set、Mapなど)
- 実装クラス(ArrayList, HashSet, HashMapなど)
というように、整理されたクラスやインターフェースの集まりが『コレクションフレームワーク』と呼ばれていると理解するとスッキリします。
このページでは、コレクションフレームワークの基礎を6つのステップに分けて解説します。初心者の方は、この順番どおりに学ぶのがおすすめです。
ステップ1:コレクションの種類を知る
コレクションフレームワークの中心となるのは、主に以下の3種類です。
- List
- 順番どおりにデータを並べる
- 同じデータを複数回追加してもOK
- 例:
ArrayList
,LinkedList
など
- Set
- 同じデータを重複して入れられない
- データの順番は保証されない場合が多い
- 例:
HashSet
,LinkedHashSet
,TreeSet
など
- Map
- 「キー(key)」と「値(value)」をペアで管理
- キーを使って素早く値を取り出せる
- 例:
HashMap
,LinkedHashMap
,TreeMap
など

初めて勉強するときは、List・Set・Mapという3つのカテゴリだけでも頭に入れておくと、整理がしやすくなります。
ステップ2:「List」って何?
Listとは、順番を重視するコレクションです。同じデータ(要素)を重複して入れることができます。
具体的には、以下のような特徴があります。
ステップ3:「Set」って何?
Setは、同じデータを重複して保持できないコレクションです。
同じ文字列や同じ数値を何度追加しても、重複分は自動的に無視されます。さらに、大半のSetでは順番が保証されないという特徴があります(ただし例外的に、LinkedHashSet
は要素を追加した順番が維持されます)。
- HashSet
- Setの代表格
- 要素の順番は保証されない
- 重複した要素を自動的に弾いてくれる
- LinkedHashSet
- 追加した順番で要素が維持される
- 順番が必要だが重複は排除したい、というときに便利
- TreeSet
- 要素を常にソート(並び替え)された状態で保持する
- ただしソートのための比較ルールを守る必要がある(
Comparable
やComparator
の実装が必要)
「同じデータが入っても困る」「一意の要素を集めたい」ときにはSetが適任です。
ステップ4:「Map」って何?
Mapは、キー(Key)と値(Value)をペアで管理するコレクションです。
例えば、果物とその値段を管理する場合、「りんご - 100円」「みかん - 80円」などといった組み合わせで保存できます。
- HashMap
- 最も一般的に使われる実装
- 順番は保証されない
- LinkedHashMap
- 要素を追加した順番で保持する
- 取り出し順序にこだわりがあるならこちら
- TreeMap
- 常にキーに応じてソートされた順番で管理
- キーが自然順序(アルファベット順など)で並ぶ
Mapを使えば、キーを使って瞬時に値を取り出すことができます。例えば「りんご」と入力すれば、対応する値段がすぐ出てくるというわけです。
ステップ5:実際にどう使う?
ここまでの話では、インターフェース(List, Set, Mapなど)の特徴ばかりを説明してきました。実際にプログラミングするときは、これらのインターフェースを実装したクラス(ArrayList
, HashSet
, HashMap
など)を使います。
Javaでは、コレクションフレームワークに関するクラスやインターフェースは多くが「java.util
」パッケージに含まれています。コードを書くときは、あらかじめ以下のような形でインポートします。
import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap;
「List<型> 変数名 = new ArrayList<>()」などのように宣言して、インスタンス化すればOKで、すぐに利用が可能になります。
ステップ6:コードで覚えよう
実際のコードを見たほうが理解が深まるので、List(ArrayList)・Set(HashSet)・Map(HashMap)の例を順番に示します。
■ List(ArrayList)のコード例
import java.util.ArrayList; import java.util.List; public class SampleList { public static void main(String[] args) { // String型のListを作る List<String> fruits = new ArrayList<>(); // 要素を追加 fruits.add("りんご"); fruits.add("みかん"); fruits.add("りんご"); // 同じ要素を追加してもOK // Listの中身を出力 System.out.println(fruits); // 出力例:[りんご, みかん, りんご] // 要素の取り出し(インデックスでアクセス) System.out.println(fruits.get(0)); // 出力例:りんご // 要素数(サイズ)の取得 System.out.println(fruits.size()); // 出力例:3 // 特定の要素を削除 fruits.remove("みかん"); System.out.println(fruits); // 出力例:[りんご, りんご] // Listが空かどうかを確認 System.out.println(fruits.isEmpty()); // falseが返る(空ではない) } }
このコード例でわかるように、List(とくにArrayList
)は「順番に沿って要素を追加したり、取り出したりする」ことがメインです。同じものを何度も追加しても問題なく格納されます。
■ Set(HashSet)のコード例
import java.util.HashSet; import java.util.Set; public class SampleSet { public static void main(String[] args) { // String型のSetを作る Set<String> fruits = new HashSet<>(); // 要素を追加 fruits.add("りんご"); fruits.add("みかん"); fruits.add("りんご"); // 重複した要素は無視される // Setの中身を出力 System.out.println(fruits); // 例:[りんご, みかん](順番は保証されない) // 要素数(サイズ)の取得 System.out.println(fruits.size()); // 例:2 // 要素の有無を確認 System.out.println(fruits.contains("りんご")); // trueが返る(りんごは入っている) // 要素を削除 fruits.remove("みかん"); System.out.println(fruits); // 例:[りんご] // 全要素を削除 fruits.clear(); System.out.println(fruits.isEmpty()); // trueが返る(すべて削除された) } }
Setでは、重複要素が排除される点と、順序が保証されない点がListとの大きな違いです。大量のデータから重複を除去したいようなケース(例:ユーザーが入力したキーワード一覧の重複を排除)で活躍します。
■ Map(HashMap)のコード例
import java.util.HashMap; import java.util.Map; public class SampleMap { public static void main(String[] args) { // Key: String, Value: Integer のMapを作る Map<String, Integer> fruitPrices = new HashMap<>(); // キーと値を追加 fruitPrices.put("りんご", 100); fruitPrices.put("みかん", 80); fruitPrices.put("バナナ", 120); // 値を取得(キーを指定) System.out.println(fruitPrices.get("りんご")); // 例:100 // Mapのサイズを取得 System.out.println(fruitPrices.size()); // 例:3 // キーの存在チェック System.out.println(fruitPrices.containsKey("みかん")); // trueが返る // 値の存在チェック System.out.println(fruitPrices.containsValue(120)); // trueが返る(120という値が存在する) // 要素を削除 fruitPrices.remove("バナナ"); System.out.println(fruitPrices); // 例:{りんご=100, みかん=80} // 全要素を削除 fruitPrices.clear(); System.out.println(fruitPrices.isEmpty()); // trueが返る } }
Mapでは「キー」と「値」をペアで管理するため、キーをもとに素早く値を取り出すことができます。「ID番号→ユーザー名」の関係や、「単語→翻訳」のように、何かしらの名称とその情報をセットにしたいときに非常に重宝します。
- Qnew ArrayList<>() <>はどういう意味・・・?
- A
これは、ArrayListに「どのようなデータ型を入れるか」を指定するためのものです。
// String型だけを入れるArrayListを作成 ArrayList<String> list = new ArrayList<>();
こう書くことで、
- このArrayListにはString型のデータだけ入れると明示的に指定できます。
- String以外の型(IntegerやDoubleなど)を入れようとすると、コンパイル時にエラーになります。
つまり、
<>
は「このコレクションにどんな型のデータを入れるのか」を指定し、型安全性を高めるために使われます。Java7以降では、右辺の<>
内は省略可能になっています(ダイヤモンド演算子と呼ばれます)。// 明示的に書く場合(冗長) ArrayList<String> list = new ArrayList<String>(); // 省略形(推奨) ArrayList<String> list = new ArrayList<>();
- QArrayList list = new ArrayList(); と書くこともできる?
- A
はい、書くことはできます。ただし、これは推奨されません。型指定(ジェネリクス)を省略すると、どんな型のデータでも入れられる状態になります。 要素を取り出す際に必ずキャストが必要になり、誤った型を取り出すと実行時に例外が発生する可能性が高まります。
ArrayList list = new ArrayList(); // 型を指定しない(非推奨) list.add("りんご"); list.add(100); // Integer型も入れられてしまう(危険) String fruit = (String) list.get(0); // キャストが必要になる int num = (int) list.get(1); // キャストが必要になる
初心者が覚えておくべきポイント(使い分け編)
ここまで紹介したList・Set・Mapそれぞれは、使いどころが異なります。迷ったときは、以下の基準を参考にしてください。
- 順序を管理したい+重複OK
- List(
ArrayList
など) - 例:表示順をそのまま保ちたい、同じデータでも繰り返し扱いたい場合
- List(
- 重複を排除したい+順序は気にしない
- Set(
HashSet
など) - 例:登録者のメールアドレス一覧をユニークに管理したい場合
- Set(
- キーと値のペアで管理したい
- Map(
HashMap
など) - 例:「商品名→価格」「従業員ID→従業員名」を関連づけたい場合
- Map(

ここまでで基本編は終了。ここから、より実践的な開発現場で必要となる知識を説明していきます。
ArrayListの基本事項
ArrayListは、Javaのコレクションフレームワークの中でも非常に使いやすく、よく使われるクラスです。ここでは、ArrayListの基本となる考え方や特徴、使い方の流れを順を追って1から説明します。
ステップ1:ArrayListとは何か?
まず、ArrayListは「動的配列」としての役割を持っています。
ステップ2:ArrayListの作成と初期容量
ArrayListを利用する際、まずはインスタンスを作成します。
- 基本的な宣言方法
ArrayList<String> list = new ArrayList<>();
この例では、文字列(String型)のArrayListを作成しています。
- 初期容量の設定
ArrayListは内部的に配列を使って要素を保持しています。もし要素が多くなると、自動で内部配列のサイズが拡大されますが、この際に新しい配列に全要素をコピーするため、パフォーマンスに影響が出ることがあります。
そのため、予め必要な容量が分かっている場合は、初期容量を設定すると効率が良くなります。
ArrayList<String> list = new ArrayList<>(100);
ステップ3:要素の追加と取り出し
ArrayListの最も基本的な操作は、要素の追加と取り出しです。
- 要素の追加
list.add("りんご"); list.add("みかん"); list.add("バナナ");
上記のように、add()
メソッドを使って順番に要素を追加できます。
- 要素の取得
インデックスを使って要素にアクセスできます。最初の要素は0番目の位置に格納されます
String firstItem = list.get(0); // ここでは "りんご" が取得される System.out.println(firstItem);
ステップ4:要素の削除や更新
ArrayListでは、必要に応じて要素の削除や更新も行えます。
- 要素の削除
指定した要素を削除する方法には、インデックスや直接値を指定する方法があります。
// 直接値で削除(最初に見つかった "みかん" を削除) list.remove("みかん"); // インデックスで削除 list.remove(0); // 最初の要素 "りんご" が削除される
注意点として、先頭や中央での削除の場合、後続の要素が前に詰められるため、要素のシフトが発生しパフォーマンスに影響することがあります。
- 要素の更新
インデックスを指定して、新しい値に変更することも可能です。
list.set(0, "グレープ"); // 0番目の要素を "グレープ" に更新
remove(int index)
の使用例と注意点
- インデックスの指定は0から始まります。
- 削除後、後続の要素は自動的に前へ詰められます。
- 範囲外のインデックスを指定すると
IndexOutOfBoundsException
が発生します。
ArrayList<String> fruits = new ArrayList<>(); fruits.add("りんご"); fruits.add("みかん"); fruits.add("バナナ"); // インデックス1(2番目)の要素「みかん」を削除 String removedFruit = fruits.remove(1); System.out.println("削除された要素: " + removedFruit); // 出力:みかん System.out.println(fruits); // 結果:[りんご, バナナ]
remove(Object o)
の使用例
このメソッドは指定した要素を探し、最初に一致したものを削除します。
- 指定した値が存在しない場合、リストは変更されず
false
を返します。 - 要素が複数ある場合でも最初に見つかった1つだけが削除されます。
ArrayList<String> fruits = new ArrayList<>(); fruits.add("りんご"); fruits.add("みかん"); fruits.add("バナナ"); fruits.add("りんご"); // 重複して追加 // 「りんご」を削除(最初の「りんご」のみ削除) boolean result = fruits.remove("りんご"); System.out.println("削除結果:" + result); System.out.println(fruits); // 出力: // 削除結果:true // [みかん, バナナ, りんご]
注意点 | 内容 | 対策 |
---|---|---|
インデックス指定による削除時の注意 | 要素の削除後、後ろの要素が詰められる(シフト)ため、処理コストが発生する | 中央や先頭の要素を頻繁に削除する場合はLinkedListを検討 |
オブジェクト指定による削除時の注意 | 同じ要素が複数ある場合でも、最初に見つかった1つしか削除されない | すべて削除したい場合はループ処理が必要 |
イテレーション中の削除 | 拡張for文などでイテレーション中にremoveするとConcurrentModificationException発生の可能性あり | Iteratorを使用した安全な削除を行う |
ステップ5:サイズの確認とリストの状態
ArrayListの状態を確認するために、サイズの取得や空かどうかのチェックを行うことができます。
- サイズの取得
int size = list.size(); System.out.println("リストのサイズ: " + size);
- 空かどうかのチェック
boolean isEmpty = list.isEmpty(); System.out.println("リストは空ですか? " + isEmpty);
ステップ6:イテレーション(反復処理)の注意点
ArrayListの要素を順に処理する際、ループ処理がよく使われます。ここで注意すべきは、イテレーション中に直接リストの要素を変更しないことです。
- 拡張for文とIterator
拡張for文(for-each)で要素を処理することは非常に簡単ですが、ループ中にリストから要素を削除するとエラーが発生する場合があります。
// NG: 拡張for文で削除するとConcurrentModificationExceptionが発生する可能性がある for (String fruit : list) { if (fruit.equals("みかん")) { list.remove(fruit); } }
- Iteratorの使用
安全に削除するためにはIteratorを使い、Iteratorのremove()
メソッドで削除します。
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); if (fruit.equals("みかん")) { iterator.remove(); // イテレーション中の安全な削除方法 } }
ステップ7:スレッドセーフ性と注意点
ArrayListは基本的にスレッドセーフではありません。つまり、複数のスレッドから同時にアクセスし、変更操作を行う場合には注意が必要です。
- スレッドセーフにする方法
Javaでは、Collections.synchronizedList()
メソッドを使うことで、ArrayListをスレッドセーフなリストとしてラップすることができます。
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
- これにより、マルチスレッド環境でも安全に利用できるようになります。ただし、すべての状況で自動的に排他制御がされるわけではないので、利用時の状況に合わせて設計する必要があります。
ステップ8:ジェネリクスの活用と型安全性
ArrayListはジェネリクスに対応しているため、リスト内に格納する要素の型を指定することができます。
- ジェネリクスを利用するメリット
型が指定されることで、コンパイル時に型チェックが行われ、間違った型のデータが格納されることを防げます。また、要素を取り出す際にキャストの必要がなくなるため、コードがシンプルになります。
ArrayList<String> fruits = new ArrayList<>(); fruits.add("りんご"); // fruits.add(100); // コンパイルエラーになる(型の不整合)
- 型指定しない場合のリスク
型指定しないと、リストから取り出した際にObject型となり、明示的にキャストする必要が出てきます。これにより、実行時にClassCastExceptionが発生するリスクが増えます。
ステップ9:実践例で理解を深める
ここまでのステップでArrayListの基本的な操作や注意点を学びました。最後に、これらをひとつの実践例としてまとめたコード例を紹介します。
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Collections; public class ArrayListPractice { public static void main(String[] args) { // 初期容量を指定してArrayListを作成 List<String> fruits = new ArrayList<>(10); // 要素の追加 fruits.add("りんご"); fruits.add("みかん"); fruits.add("バナナ"); fruits.add("みかん"); // 重複もOK // ArrayListの状態を表示 System.out.println("初期のリスト: " + fruits); System.out.println("リストのサイズ: " + fruits.size()); // 特定の要素の取得 System.out.println("0番目の要素: " + fruits.get(0)); // Iteratorを使って"みかん"を削除 Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); if (fruit.equals("みかん")) { iterator.remove(); } } System.out.println("削除後のリスト: " + fruits); // インターフェースで宣言しているので、必要に応じてListの実装を変更できる List<String> syncList = Collections.synchronizedList(new ArrayList<>()); syncList.add("スレッドセーフなりんご"); System.out.println("同期リスト: " + syncList); } }
HashMapの基本事項
次に、Javaのコレクションフレームワークの中でもよく使われる「HashMap」について解説します。
HashMapは、キーと値のペアでデータを管理できる非常に便利なクラスです。ここでは、その基本的な使い方や注意点、実践例を紹介します。
ステップ1:HashMapとは何か?
HashMapは、キーと値をセットで管理するためのコレクションです。
キーを使って値を高速に取り出すことができ、例えば「社員ID」と「社員名」や「商品コード」と「商品の価格」など、関連付けたデータを管理するのに最適です。
- キー:一意である必要があります。重複したキーは登録できません。
- 値:キーに対応するデータ。値は重複しても問題ありません。
- 内部的にはハッシュテーブルを使用しており、キーのハッシュ値に基づいてデータが格納されます。
ステップ2:HashMapの作成と基本的な宣言
HashMapを使用するには、まずインスタンスを作成します。
ジェネリクスを用いることで、キーと値の型を明示的に指定し、型安全に操作できます。
// キーがString型、値がInteger型のHashMapを作成 HashMap<String, Integer> map = new HashMap<>();
この例では、例えば「りんご」と「その価格」を管理する場合に活用できます。
ステップ3:要素の追加
HashMapにデータを追加するには、put()
メソッドを使用します。
キーと値をペアで登録するため、同じキーで別の値を追加すると、既存の値が上書きされます。
map.put("りんご", 100); map.put("みかん", 80); map.put("バナナ", 120);
ここで「りんご」というキーが既に存在している場合、再度put("りんご", 新しい値)
を呼び出すと、値が更新されます。
ステップ4:値の取得
登録した値は、キーを指定して高速に取得できます。get()
メソッドを利用し、指定したキーに対応する値を取り出します。
int price = map.get("りんご"); System.out.println("りんごの価格は " + price + "円です。");
キーが存在しない場合、null
が返されるため、取得結果のチェックが必要な場合があります。
ステップ5:要素の削除と更新
HashMapから要素を削除するには、remove()
メソッドを使用します。また、キーに対応する値を更新するには、再度put()
を利用して上書きします。
// キー「みかん」に対応する要素を削除 map.remove("みかん"); // キー「りんご」の値を更新 map.put("りんご", 110);
このように、HashMapはキーに対応するデータの管理や更新がシンプルに行えます。
ステップ6:サイズの確認と存在チェック
HashMapの現在の要素数を確認するにはsize()
メソッドを、キーまたは値の存在確認にはcontainsKey()
やcontainsValue()
メソッドを使用します。
System.out.println("マップのサイズ: " + map.size()); System.out.println("キー「りんご」が存在するか? " + map.containsKey("りんご")); System.out.println("値110が存在するか? " + map.containsValue(110));
ステップ7:イテレーション(反復処理)の方法
HashMapの全要素を反復処理(イテレーション)する場合、entrySet()
を利用すると効率的です。
これにより、キーと値のペアに一度にアクセスすることができます。
for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); }
これにより、HashMap内のすべてのキーと値の組み合わせを順に処理できます。
ステップ8:注意すべきポイント
HashMapを使用する際の注意点をいくつか紹介します。
- キーの一意性:同じキーを複数登録すると、後から登録した値で上書きされます。キーはユニークである必要があります。
- nullの取り扱い:HashMapではキーや値にnullを許容しますが、キーにnullを複数回設定すると、後の値で上書きされるため注意が必要です。
- 順序保証がない:HashMapは内部的にハッシュテーブルを使用しているため、登録した順序は保持されません。順序を保証したい場合は、LinkedHashMapを検討してください。
- スレッドセーフ性:HashMapはスレッドセーフではありません。マルチスレッド環境で複数のスレッドからアクセスする場合は、ConcurrentHashMapなどを利用するか、外部で同期処理を行う必要があります。
ステップ9:実践例で理解を深める
最後に、HashMapの基本的な操作をひとまとめにした実践例を紹介します。
このコード例を実際に動かしてみることで、HashMapの動作や使い方、注意点を体感してください。
import java.util.HashMap; import java.util.Map; public class HashMapPractice { public static void main(String[] args) { // HashMapの作成(キーがString型、値がInteger型) HashMap fruitPrices = new HashMap<>(); // 要素の追加 fruitPrices.put("りんご", 100); fruitPrices.put("みかん", 80); fruitPrices.put("バナナ", 120); // キー「りんご」が既に存在する場合、値が上書きされる fruitPrices.put("りんご", 110); // 要素の取得 System.out.println("りんごの価格は " + fruitPrices.get("りんご") + "円です。"); // サイズの確認 System.out.println("マップのサイズ: " + fruitPrices.size()); // キーと値の存在チェック if (fruitPrices.containsKey("みかん")) { System.out.println("みかんはマップに存在します。"); } // イテレーションで全要素を表示 for (Map.Entry entry : fruitPrices.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } // 要素の削除 fruitPrices.remove("バナナ"); System.out.println("削除後のマップ: " + fruitPrices); } }