JavaのHashSet入門:重複を許さない最強データ構造!
こんにちは。ゆうせいです。
今回は、Javaの「HashSet(ハッシュセット)」というコレクションについて、新人エンジニアの皆さんに向けて丁寧に解説します。
「リストはわかるけど、セットって何?」「HashMapとどう違うの?」と疑問に思っている方も多いかもしれません。
HashSetは、「重複のないデータを集めるときに最強の道具」なんです!
HashSetとは何か?
H3: 「集合」を扱うためのクラス
HashSet
は、JavaのSetインターフェースを実装した代表的なクラスです。
特徴まとめ
特徴 | 内容 |
---|---|
順序保証なし | 要素の順番は保持されない |
重複不可 | 同じ値を2回入れても1つしか残らない |
nullを1つだけ許す | null を複数入れても1つしか保存されない |
高速な検索 | 値の存在確認が高速(内部でHashMapを使っている) |
ArrayListとの違いは?
例えるなら:
- ArrayListは「順番を重視したノート」
- HashSetは「名前がダブらない出席簿」
つまり、「順番は気にしないけど、同じものは入れたくない」ときに使うのがSet系。
HashSetの基本的な使い方
import java.util.HashSet;
import java.util.Set;
Set<String> names = new HashSet<>();
names.add("田中");
names.add("佐藤");
names.add("田中"); // 重複している
System.out.println(names); // [田中, 佐藤](順序は保証されない)
「田中」が2回追加されても、1回しか入らない!
ここがリストとの最大の違いです。
よく使うメソッド一覧
メソッド名 | 説明 |
---|---|
add() | 要素を追加(重複は無視される) |
remove() | 指定した要素を削除 |
contains() | 要素が含まれているか判定 |
size() | 要素数を取得 |
clear() | 全ての要素を削除 |
isEmpty() | 空かどうかを判定 |
forEach() or for-each 文 | 繰り返し処理 |
使用例と出力結果
Set<Integer> numbers = new HashSet<>();
numbers.add(10);
numbers.add(20);
numbers.add(10);
System.out.println(numbers); // [20, 10](順番は毎回異なる)
System.out.println(numbers.contains(20)); // true
numbers.remove(10);
System.out.println(numbers.size()); // 1
図で理解するHashSet
操作 | Setの状態 |
---|---|
add(1) | {1} |
add(2) | {1, 2} |
add(1) | {1, 2}(重複は無視) |
remove(2) | {1} |
注意点と落とし穴
順序に意味がある処理では使わない!
HashSetは要素の順番がランダムになります。
並び替えたいときは TreeSet
や LinkedHashSet
を使いましょう。
equals() と hashCode() が重要!
HashSetは内部的にハッシュ値(hashCode)を使って重複チェックをしています。
クラス独自のオブジェクトを使うときは、以下を必ずオーバーライドしてください:
@Override
public boolean equals(Object o) {
// 同一性の定義
}
@Override
public int hashCode() {
// ハッシュ値の計算
}
どんなときに使うの?
- メールアドレスのように重複を許したくないデータを管理するとき
- データの一意性(ユニークさ)を守りたいとき
- リストから重複を取り除く処理
例:リストから重複を削除
List<String> list = Arrays.asList("A", "B", "A", "C");
Set<String> unique = new HashSet<>(list);
System.out.println(unique); // [A, B, C]
HashSetのメリット・デメリット
メリット | デメリット |
---|---|
高速な検索・追加・削除 | 要素の順番が保証されない |
重複を自動的に排除 | 順序を保ちたいなら不向き |
nullも入れられる(1つまで) | equals/hashCodeの実装が必要な場合あり |
Setの集合演算って何?
Set同士を合体させたり、共通部分を取り出したり、一方から他方を引いたりする操作可能です。「集合演算」といいます。
用語 | 演算の意味 | 例 |
---|---|---|
和集合(union) | すべての要素をまとめる | A ∪ B |
積集合(intersection) | 共通の要素だけ取り出す | A ∩ B |
差集合(difference) | 片方にしかない要素を取り出す | A − B |
具体例で学ぼう!
2つのセット A
と B
があるとします:
Set<String> A = new HashSet<>(Arrays.asList("赤", "青", "緑"));
Set<String> B = new HashSet<>(Arrays.asList("青", "黄", "黒"));
このとき、次の操作が可能になります。
1. 和集合(union): 全部をまとめる
Set<String> union = new HashSet<>(A); // Aをコピー
union.addAll(B); // Bの要素を全部追加
System.out.println(union); // [赤, 青, 緑, 黄, 黒]
重複なしで全ての要素を含んだ集合になります。
例え:
「赤青緑」のクラスAと「青黄黒」のクラスBが合同授業を受ける → 重複なしで5人!
2. 積集合(intersection): 共通の要素だけ
Set<String> intersection = new HashSet<>(A);
intersection.retainAll(B);
System.out.println(intersection); // [青]
両方の集合に共通する要素だけが残る!
例え:
クラスAとBの両方にいる「青」さんだけが共通メンバー。
3. 差集合(difference): 一方だけにある要素
Set<String> difference = new HashSet<>(A);
difference.removeAll(B);
System.out.println(difference); // [赤, 緑]
これは「Aにだけ存在し、Bには存在しない要素」を意味します。
例え:
クラスAにはいるけど、クラスBにはいないメンバー。
よくある落とし穴と注意点
retainAll()
やremoveAll()
は、元の集合を変更します!- 元を残したいときはコピー(
new HashSet<>(元集合)
)を作ること。
- 元を残したいときはコピー(
Set
の要素には順序がないため、出力順序がバラバラになることもあります。- nullを含むSetでも操作できますが、nullの扱いには注意!
応用編:リストから重複なしで共通項を取得
List<String> list1 = Arrays.asList("A", "B", "C", "A");
List<String> list2 = Arrays.asList("B", "C", "D");
Set<String> common = new HashSet<>(list1);
common.retainAll(list2);
System.out.println(common); // [B, C]
JavaのSet集合演算をマスターすれば、重複除去やグループ間の比較処理が驚くほど簡単になります。業務でも使う場面が多いので、新人エンジニアの皆さんもしっかり身につけておきましょう!
セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク
投稿者プロフィール
