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は要素の順番がランダムになります。
並び替えたいときは TreeSetLinkedHashSet を使いましょう。

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つのセット AB があるとします:

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集合演算をマスターすれば、重複除去やグループ間の比較処理が驚くほど簡単になります。業務でも使う場面が多いので、新人エンジニアの皆さんもしっかり身につけておきましょう!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。