AIによる要約
HashSet.addは、要素が新しく追加されSetの内容が変わった場合にtrue、同じ要素がすでに存在して追加されなかった場合にfalseを返します。重複を検知するだけならcontainsしてからaddする二段階処理ではなく、addの戻り値を使えます。ただし、自作クラスの重複判定はequalsとhashCodeに依存します。入力順を保つならLinkedHashSetも検討します。
この記事はXへ投稿したJavaクイズの詳しい解説です。まず問題を解きたい方は、Xの元ポストを開くことができます。


Setは重複を持たないコレクションとして学びますが、addの戻り値まで使う機会は少ないかもしれません。CSV取込や一括登録では、このbooleanが重複行を見つける簡潔な手段になります。
ただし、文字列では期待通りでも自作DTOでは重複を検知できないことがあります。Setが何を同じ要素とみなすかは、equalsとhashCodeで決まるためです。
この記事のポイント
- 新規追加ならtrue、既存要素ならfalseを返す
- containsしてからaddする必要はない
- 自作クラスの重複判定はequalsとhashCodeに依存する
- 入力順が必要ならLinkedHashSetを検討する
- 重複を無視するかエラーにするかは業務仕様で決める
まずはXのJavaクイズを確認する

Set<String> names = new HashSet<>();
System.out.println(names.add("A"));
System.out.println(names.add("A"));答えはtrue、falseです。最初のAは追加され、2回目はすでに存在するため追加されません。

addのbooleanはSetが変化したかを表す
Collectionのaddは、呼び出しによってコレクションの内容が変わった場合にtrueを返します。Setでは同じ要素を追加できないため、既存要素ならfalseです。例外ではなく正常な結果です。
Set<String> codes = new HashSet<>();
boolean first = codes.add("A01"); // true
boolean second = codes.add("A01"); // false
System.out.println(codes); // [A01]戻り値を使わずaddすることもできますが、重複を業務上検知したいなら捨てずに分岐へ使います。
containsしてからaddしなくてよい
重複確認のためにcontainsを呼び、その後addするコードをよく見ます。しかしadd自体が重複確認を行い、結果を返します。
// 冗長な例
if (codes.contains(code)) {
duplicateCodes.add(code);
} else {
codes.add(code);
}
// 戻り値を使う例
if (!codes.add(code)) {
duplicateCodes.add(code);
}短くすることだけが目的ではありません。『追加を試し、追加できなかったら重複として記録する』という一連の意図が1か所にまとまります。
自作クラスではequalsとhashCodeが必要
HashSetはhashCodeで候補位置を探し、equalsで同じ要素か確認します。両方を適切に実装していないDTOを追加すると、同じ社員番号でも別要素として残ることがあります。
record EmployeeKey(String companyCode, String employeeNo) {}
Set<EmployeeKey> keys = new HashSet<>();
System.out.println(keys.add(new EmployeeKey("C01", "E001"))); // true
System.out.println(keys.add(new EmployeeKey("C01", "E001"))); // falserecordは構成要素に基づくequalsとhashCodeを生成します。通常クラスで実装する場合は、何を同一性に含めるか決め、可変項目を安易に使わないでください。
CSV取込で重複行を検知する
一括取込では、DBへ問い合わせる前にファイル内の重複を見つけられます。行番号も合わせて保持すると、利用者へ修正箇所を返しやすくなります。
Set<String> seenOrderNos = new HashSet<>();
List<Integer> duplicateRows = new ArrayList<>();
for (int i = 0; i < rows.size(); i++) {
String orderNo = rows.get(i).orderNo();
if (!seenOrderNos.add(orderNo)) {
duplicateRows.add(i + 2); // ヘッダー分を加味
}
}Setで重複を消して正常処理を続けるのか、重複行をエラーとして全件中止するのかは仕様次第です。黙って消すと、利用者が投入した件数と登録件数が合わず問い合わせになります。
順序が必要ならLinkedHashSet
HashSetの走査順を入力順として利用してはいけません。重複を除きつつ最初に出現した順序を維持したいならLinkedHashSetを使います。
Set<String> unique = new LinkedHashSet<>(
List.of("B", "A", "B", "C"));
System.out.println(unique); // [B, A, C]並び順がコード順や名前順という仕様なら、LinkedHashSetではなくソートが必要です。『たまたま同じ順』をテストで期待値にしないようにします。
nullと可変オブジェクトの注意
HashSetはnullを1つ保持できますが、nullを有効な要素として扱うかは別問題です。また、追加後にequals・hashCodeへ使う項目を書き換えると、containsやremoveで見つけられなくなることがあります。
Set<User> users = new HashSet<>();
User user = new User("U001");
users.add(user);
user.setId("U999"); // hashCodeに使う値を変更
System.out.println(users.contains(user)); // 期待どおりにならない可能性Setの要素として使う識別値は不変にするか、変更前にremoveして変更後にaddし直します。可能なら識別子専用の不変クラスを使います。
JUnitで戻り値と最終状態を確認する
addのbooleanだけでなく、最終的な要素数と内容を確認します。独自クラスでは同一・非同一になる境界もテストします。
@Test
void 同じ社員番号は2回追加できない() {
Set<EmployeeKey> keys = new HashSet<>();
assertTrue(keys.add(new EmployeeKey("C01", "E001")));
assertFalse(keys.add(new EmployeeKey("C01", "E001")));
assertEquals(1, keys.size());
}現場レビューでよくある指摘
Setのレビューでは重複が消えたかだけでなく、何を同じとみなし、重複を利用者へどう返すかを確認します。
// レビューコメント例
containsの後にaddしていますが、addの戻り値で重複を判定できます。
判定と追加を1か所へまとめてください。
// レビューコメント例
EmployeeにequalsとhashCodeがないため、同じ社員番号でも重複判定できません。
業務上の同一性を決めて両方を実装してください。
// レビューコメント例
HashSetの走査順へ依存しています。入力順を残す仕様ならLinkedHashSet、
コード順なら明示的なソートを使ってください。Setへ変えれば重複問題が自動的に解決するわけではありません。同一性、順序、重複時の扱いを仕様として説明し、テストへ固定します。
提出前のセルフチェック
レビュー前に確認すること
- addの戻り値を重複判定に使えるか
- containsとaddを重ねていないか
- equalsとhashCodeが同じ項目を使うか
- Set追加後に識別項目を変更しないか
- 重複を無視・警告・エラーのどれにするか
- 順序要件に合うSetを選んだか
- null要素を許可する必要があるか
- 戻り値と最終要素数をテストしたか
コレクションとequalsを実践的に学ぶ参考書
Setの挙動はequals、hashCode、可変性とつながります。Javaの基本コレクションを整理した上で、値オブジェクトの設計まで学ぶと実務での判断が安定します。
スッキリわかるJava入門 実践編 第5版
基礎文法の次に必要な、現場寄りのJava知識を補う。
コレクション、ジェネリクス、ラムダ式、ストリームなど、業務コードで出会いやすい機能を入門編の次に学べます。
- Java基礎の次に何を学ぶか迷っている
- コレクションやStreamを整理したい
当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。
この記事とあわせて読みたい
まとめ
HashSet.addは新しい要素が追加されたときtrue、すでに同じ要素があり内容が変わらないときfalseを返します。重複検知にはcontainsとaddを重ねず、この戻り値を使えます。
自作クラスではequalsとhashCode、入力順が必要ならLinkedHashSet、重複時に無視するかエラーにするかという業務仕様まで確認してください。
