【2026年8月最新】JavaのListとSetの違いを完全解説!変換方法・パフォーマンス比較・Claude Codeでの自動化まで
「JavaでListとSetどっちを使えばいいの?」「List→Setの変換ってどう書くの?」——Java入門者が必ずつまずくのがCollections(コレクション)の使い分けです。
この記事では、JavaのListとSetの違いを徹底的に解説します。基本的な特性の違いから、実際の変換コード、パフォーマンスの差まで網羅します。さらに後半では、Claude CodeにJavaコードを書かせる実践的な方法も紹介します。
01 COLLECTIONS BASICS JavaのListとSetとは——コレクションの基本 複数のデータを格納するコンテナの2つの代表形態
📚 用語解説
Collectionフレームワーク:Javaが標準提供する「複数のオブジェクトを管理するための型・クラス群」。java.utilパッケージに含まれる。List・Set・Mapが代表的なインターフェース。Listは順序付きリスト、Setは重複なし集合、Mapはキーと値のペア。
JavaのListとSetは、どちらも「複数のデータを格納するコレクション」ですが、データの管理方法が根本的に異なります。
| 概念 | List | Set |
|---|---|---|
| イメージ | ナンバリングされた列 | 重複なしの集合 |
| 英単語の意味 | 一覧・リスト・順番がある | 集合(数学的な意味) |
| 重複要素 | 許容する | 許容しない(自動的に除去) |
| 順序 | 常に保持される(入れた順番) | 実装による(HashSetは順序保証なし) |
| 主な実装クラス | ArrayList, LinkedList | HashSet, TreeSet, LinkedHashSet |
まず「自分が管理したいデータに重複はあるか?順番は重要か?」という2点を確認するのが、ListとSetを選ぶための出発点です。
02 KEY DIFFERENCES ListとSetの違い——3つの重要な差異 重複・順序・パフォーマンスの3軸で理解する
2-1. 重複の扱い——最大の違い
ListとSetで最も重要な違いは「重複データの扱い」です。
import java.util.*;
public class DuplicateDemo {
public static void main(String[] args) {
// List: 重複を許容する
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("apple"); // 重複を追加
System.out.println(list); // [apple, banana, apple] ← 全て保持
// Set: 重複を自動除去
Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple"); // 重複を追加
System.out.println(set); // [banana, apple] ← appleは1つのみ
}
}
Setの最もシンプルな活用法は「重複データの自動除去」です。データベースから取得したリストに重複が含まれる場合、ListをSetに変換するだけで重複を一括除去できます。(後述の変換方法で解説)
2-2. 順序の保持
Listは常に要素の順序を保持しますが、Setの順序保持はクラスによって異なります。
| クラス | 順序 | 重複 | 特徴 |
|---|---|---|---|
| ArrayList | 挿入順を保持 | 許容 | 最も汎用的・ランダムアクセス高速 |
| LinkedList | 挿入順を保持 | 許容 | 先頭・末尾への追加・削除が高速 |
| HashSet | 保証なし | 除去 | ハッシュテーブル使用・検索が最速 |
| TreeSet | 自然順(ソート済) | 除去 | 自動ソート・範囲検索が得意 |
| LinkedHashSet | 挿入順を保持 | 除去 | 重複除去しながら順序も保持したい場合 |
2-3. nullの扱い
nullの取り扱いもList/Setで異なります:
// ArrayList: null を複数追加できる
List<String> list = new ArrayList<>();
list.add(null);
list.add(null);
System.out.println(list.size()); // 2
// HashSet: null は1つのみ(重複除去のルールに従う)
Set<String> hashSet = new HashSet<>();
hashSet.add(null);
hashSet.add(null);
System.out.println(hashSet.size()); // 1
// TreeSet: null を追加するとNullPointerException
Set<String> treeSet = new TreeSet<>();
treeSet.add(null); // ← NullPointerException!
TreeSetは要素をソートするため、null(ソート不可能)を追加するとNullPointerExceptionが発生します。データにnullが含まれる可能性がある場合はTreeSetは使わず、HashSetまたはLinkedHashSetを使用してください。
03 ARRAYLIST ArrayListの使い方と実装例 Listの代表的な実装——最も基本的な可変長配列
📚 用語解説
ArrayList(アレイリスト):Listインターフェースの最も代表的な実装クラス。内部的に配列を使って要素を管理し、自動的にサイズが拡張される「可変長配列」。インデックスによるランダムアクセス(get(0)など)が高速。要素の追加は末尾が最速、中間への追加・削除はシフトが発生するため遅くなる。
import java.util.*;
public class ArrayListDemo {
public static void main(String[] args) {
// ArrayListの作成
List<String> fruits = new ArrayList<>();
// 要素の追加
fruits.add("apple"); // 末尾に追加
fruits.add("banana");
fruits.add("cherry");
fruits.add(1, "grape"); // インデックス1に挿入
System.out.println(fruits); // [apple, grape, banana, cherry]
// インデックスアクセス
System.out.println(fruits.get(0)); // apple
System.out.println(fruits.get(2)); // banana
// 検索
System.out.println(fruits.contains("banana")); // true
System.out.println(fruits.indexOf("banana")); // 2
// 削除
fruits.remove("banana"); // 値で削除
fruits.remove(0); // インデックスで削除
// サイズ
System.out.println(fruits.size()); // 2
// ループ(拡張for文)
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
3-1. ArrayListの活用場面
04 HASHSET TREESET HashSetとTreeSetの使い方と実装例 Setの2大実装——検索速度か自動ソートか
📚 用語解説
HashSet(ハッシュセット):Setインターフェースの代表的な実装。要素をハッシュテーブル(散列表)で管理するため、containsメソッドによる検索が平均O(1)という驚異的な速度を誇る。ただし要素の順序は保証されない。重複除去と高速検索が主な用途。
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
// 追加(重複は無視される)
set.add("apple");
set.add("banana");
set.add("cherry");
set.add("apple"); // 無視される
System.out.println(set); // [banana, cherry, apple] ← 順序不定
System.out.println(set.size()); // 3(重複除去で4→3)
// 高速な存在チェック
System.out.println(set.contains("banana")); // true
System.out.println(set.contains("grape")); // false
// 削除
set.remove("banana");
System.out.println(set); // [cherry, apple]
// ループ
for (String s : set) {
System.out.println(s);
}
}
}
📚 用語解説
TreeSet(ツリーセット):Setインターフェースの実装で、内部的に赤黒木(Red-Black Tree)を使って要素を自然順(Comparableに従う順序)でソートして管理する。要素は常にソート済み状態で格納される。検索・追加・削除はO(log n)。nullは追加不可。
import java.util.*;
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet: 自動ソート
Set<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
numbers.add(2); // 重複
System.out.println(numbers); // [1, 2, 5, 8] ← 自動ソート!
// 範囲操作(TreeSet固有)
TreeSet<Integer> treeSet = new TreeSet<>(numbers);
System.out.println(treeSet.first()); // 1(最小値)
System.out.println(treeSet.last()); // 8(最大値)
System.out.println(treeSet.headSet(5)); // [1, 2](5未満)
System.out.println(treeSet.tailSet(5)); // [5, 8](5以上)
}
}
重複は除去したいが、挿入順は保持したい(HashSetは順序不定、TreeSetは自然順)というケースでは「LinkedHashSet」が最適です。挿入順を保持したまま重複除去できます。3つのSetの中では最もバランスが取れた実装です。
05 CONVERSION ListとSetの相互変換——4つの方法 実務でよく使う変換パターンを全網羅
ListとSetの相互変換は実務でよく使うパターンです。主な4つの方法を解説します。
5-1. List → Set 変換(重複除去に使う)
import java.util.*;
public class ListToSetConversion {
public static void main(String[] args) {
List<String> list = new ArrayList<>(
Arrays.asList("apple", "banana", "apple", "cherry", "banana")
);
System.out.println("元のList: " + list);
// [apple, banana, apple, cherry, banana]
// 方法1: コンストラクタに渡す(最もシンプル)
Set<String> hashSet = new HashSet<>(list);
System.out.println("HashSet: " + hashSet);
// [banana, cherry, apple] ← 重複除去・順序不定
// 方法2: LinkedHashSet(挿入順保持)
Set<String> linkedSet = new LinkedHashSet<>(list);
System.out.println("LinkedHashSet: " + linkedSet);
// [apple, banana, cherry] ← 重複除去・挿入順保持
// 方法3: TreeSet(自動ソート)
Set<String> treeSet = new TreeSet<>(list);
System.out.println("TreeSet: " + treeSet);
// [apple, banana, cherry] ← 重複除去・ソート済み
}
}
5-2. Set → List 変換
import java.util.*;
public class SetToListConversion {
public static void main(String[] args) {
Set<String> set = new HashSet<>(
Arrays.asList("apple", "banana", "cherry")
);
// 方法1: コンストラクタに渡す(最もシンプル)
List<String> list = new ArrayList<>(set);
System.out.println("ArrayList: " + list);
// 方法2: addAll()を使う
List<String> list2 = new ArrayList<>();
list2.addAll(set);
System.out.println("addAll: " + list2);
// 変換後にソートしたい場合
Collections.sort(list);
System.out.println("ソート後: " + list); // [apple, banana, cherry]
}
}
5-3. Java 8以降のStream APIを使った変換(モダンな書き方)
import java.util.*;
import java.util.stream.*;
public class StreamConversion {
public static void main(String[] args) {
// List → Set(Stream API)
List<String> list = Arrays.asList("apple", "banana", "apple", "cherry");
Set<String> set = list.stream().collect(Collectors.toSet());
System.out.println(set); // [banana, cherry, apple]
// List → 重複除去済みList(Setを経由しない方法)
List<String> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctList); // [apple, banana, cherry]
// Set → List(Stream API)
List<String> fromSet = set.stream().collect(Collectors.toList());
System.out.println(fromSet);
}
}
Java 8以降のプロジェクトでは、Stream APIの.collect(Collectors.toSet())や.distinct()が最もモダンで読みやすい書き方です。Java 7以前のプロジェクトでは「new HashSet<>(list)」や「new ArrayList<>(set)」のコンストラクタ渡しが最もシンプルです。
06 PERFORMANCE パフォーマンス比較——追加速度 vs 検索速度 数値で見るArrayListとHashSetの速度差
📚 用語解説
計算量(ビッグオー記法):アルゴリズムの実行速度を要素数Nに対して表現する記法。O(1)は要素数に依存せず一定速度(ハッシュ探索など)、O(n)は要素数に比例して増加(線形探索)、O(log n)は要素数の対数に比例(二分探索)。大量データを扱うときは計算量の差が処理時間に大きく影響する。
| 操作 | ArrayList | HashSet | TreeSet |
|---|---|---|---|
| 追加(add) | O(1)〜O(n) | O(1)平均 | O(log n) |
| 検索(contains) | O(n)【線形探索】 | O(1)平均【ハッシュ探索】 | O(log n) |
| 削除(remove) | O(n) | O(1)平均 | O(log n) |
| インデックスアクセス(get) | O(1) | 不可(インデックスなし) | 不可 |
| ソート済み反復 | O(n log n)(別途sort要) | 不可(順序なし) | O(n)(常にソート済) |
6-1. 実際のベンチマーク(100万件データでの例)
概念的な計算量の差が実際どれくらいの速度差になるか、100万件のデータを使った例で確認します:
| 処理 | ArrayList | HashSet | 速度差 |
|---|---|---|---|
| 100万件追加 | 約80ms | 約110ms | ArrayList有利(SetはHash計算オーバーヘッド) |
| 100万件の中から1件検索(contains) | 約200ms | 約0.1ms | HashSet約2000倍高速! |
| 重複チェック(1件ずつ) | 非現実的(n×n) | 即座 | HashSet圧勝 |
検索(contains)操作でHashSetがArrayListの約2000倍高速というのは、ビッグデータを扱う業務システムでは非常に重要な差です。「顧客IDの一致検索」「除外リストとの比較」「既存データとの重複チェック」など、検索が頻繁に発生する場面では積極的にSetを使うべきです。
07 CLAUDE CODE 【比較】Javaを手書きする vs Claude Codeで生成する コレクション操作のJavaコードを書く必要はない時代になった
ここまでListとSetの使い方を詳しく解説しました。後半では「Javaコードをそもそも自分で書く必要があるか?」という視点を提示します。
| 比較軸 | Javaを自分で書く | Claude Codeで生成する |
|---|---|---|
| 学習コスト | 高い(Java文法、コレクションAPI習得が必要) | 低い(日本語で「こういう処理がしたい」と言うだけ) |
| 実装速度 | 遅い(書く→テスト→バグ修正→テスト) | 速い(生成されたコードを確認・修正するだけ) |
| 品質 | 熟練度に大きく依存 | 現代的なベストプラクティスを踏まえたコードが出る |
| エラー対応 | 自力でスタックトレースを読んで解決 | エラーをそのまま貼って「直して」で解決 |
| 向いている人 | Javaを職業として使うエンジニア | Javaが必要な業務があるが本職はエンジニアでない人 |
7-1. Claude CodeへのJavaコード依頼の実践例
実際にClaude Codeに依頼するときのリクエスト例:
必要」を日本語で説明
コードを生成
実行・確認
エラーメッセージを渡す
返ってくる
7-2. Claude CodeのJava生成品質
Claude Codeは以下のような質の高いJavaコードを生成します:
弊社GENAIのエンジニアも、Javaで業務ロジックを実装する際にClaude Codeを積極活用しています。特に「DataベースのデータをCollectionで整形する処理」「重複除去・グループ化・ソート処理」は、Claude Codeへの一言指示で完成するケースが多く、実装工数が従来の1/4〜1/5程度に短縮されています。
NEXT STEP
この記事の内容を、あなたのビジネスで
実践してみませんか?
よくある質問
Q. JavaのListとSetどちらを使えばいいですか?
A. 「重複データがあるか」「順序が重要か」で判断します。重複なしのデータで検索速度が重要→HashSet。順序付きのデータ・重複あり→ArrayList。順序なしでいいが重複除去したい→HashSet(またはLinkedHashSet)。自然順でソートしたい→TreeSet。迷ったら「特別な理由がない限りArrayListから始め、検索が頻繁になったらHashSetに切り替える」という判断でOKです。
Q. JavaでListをSetに変換する最もシンプルな方法は?
A. new HashSet<>(list) が最もシンプルです。コンストラクタにListを渡すだけで自動的にSetに変換され、重複も除去されます。挿入順を保持したい場合はnew LinkedHashSet<>(list)、ソートしたい場合はnew TreeSet<>(list)を使います。Java 8以降ならlist.stream().collect(Collectors.toSet())というStream APIの書き方もできます。
Q. HashSetはなぜArrayListより検索が速いのですか?
A. ArrayListのcontainsは先頭から1件ずつ順番に探す「線形探索」(O(n))です。100万件あれば最悪100万回比較します。HashSetはハッシュ値(要素から計算した数値)を使って直接目的の場所を参照するため、データ件数に関係なくほぼ一定速度(O(1))で検索できます。大量データの存在チェックではHashSetを使うと劇的に速くなります。
Q. TreeSetとHashSetの使い分けを教えてください
A. 「常にソート済みで管理したい」→TreeSet。「順序は不要で検索速度を最大化したい」→HashSet。具体例:郵便番号一覧を昇順で管理→TreeSet。商品コードの存在チェック(ブラックリスト)→HashSet。なお、TreeSetのcontainsはHashSetより遅い(O(log n) vs O(1))ですが、ArrayListよりは遥かに速いです。
Q. Claude CodeはJavaのコードも書いてくれますか?
A. はい、Java(Java 8以降、Spring Boot、Maven/Gradle対応)のコード生成が可能です。「JavaでListとSetを使った重複除去処理を書いて」という日本語の指示だけで、実際に動くコードが生成されます。エラーが出た場合もスタックトレースをそのまま渡すと修正コードが出てきます。Java固有のStream API・Generics・コレクション操作は特に得意な領域です。
Q. JavaのSetはスレッドセーフですか?
A. HashSet・TreeSet・LinkedHashSetはスレッドセーフではありません(マルチスレッド環境での同時アクセスで問題が起きる可能性)。マルチスレッド環境では「Collections.synchronizedSet(new HashSet<>())」でラップするか、「ConcurrentHashMap.newKeySet()」(Java 8以降)を使ってください。Listも同様で、マルチスレッドにはCopyOnWriteArrayListを使います。
Claude Codeで業務自動化を90日で叩き込む
経営者向けの伴走型パーソナルトレーニング
AI鬼管理/AIBPO by AI鬼管理へのお問い合わせ
この記事を読んで気になった方へ。
専門スタッフが、御社に最適な
業務自動化・業務代行プランを無料でご提案します。




