【Java学習|実務向け】実務で差がつく!Java Stream.distinct() を使った効率的な重複除去と注意点

1. 導入:なぜStream.distinct()が重要なのか

実務におけるデータ処理では、外部APIからのレスポンスやDBからの検索結果など、重複するデータを含むリストを扱う機会が多々あります。手動でforループを回して重複チェックを行うコードは冗長でミスを誘発しやすく、また可読性も下がります。Java 8以降で導入されたStream.distinct()を利用すれば、宣言的かつ簡潔にこの課題を解決でき、コードの保守性を劇的に向上させることが可能です。

2. 基礎知識:distinct()の仕組み

Stream.distinct()は、Stream APIが提供する中間操作の一つです。このメソッドは、Objectクラスのequals()とhashCode()を内部的に使用して重複を判定します。つまり、対象となるオブジェクトが適切にこれらのメソッドをオーバーライドしていることが、正しく動作するための前提条件となります。Setコレクションと同様の仕組みで重複を排除するため、非常に効率的です。

3. 実装と解決策

基本的には、リストをストリームに変換し、distinct()を呼び出し、collect()で再度リストに戻すというフローが一般的です。もし特定のフィールド(例:ID)のみを基準に重複を除去したい場合は、カスタムComparatorを用いるか、あるいは一度Mapに変換してキーで重複を排除する「Mapのキーによる抽出」といった手法を組み合わせるのが実務上の定石です。

4. サンプルプログラム

以下は、ユーザー名を持つUserクラスのリストから、名前が重複している要素を取り除く実用的なコード例です。

import java.util.;
import java.util.stream.Collectors;

public class DistinctExample {
public static void main(String[] args) {
// テスト用データ:重複を含むリスト
List users = Arrays.asList(
new User(1, “佐藤”),
new User(2, “鈴木”),
new User(3, “佐藤”) // 名前が重複
);

// stream.distinct()を利用した重複除去
// 注意:UserクラスでequalsとhashCodeを実装している必要がある
List distinctUsers = users.stream()
.distinct()
.collect(Collectors.toList());

// 結果の確認
distinctUsers.forEach(u -> System.out.println(u.getName()));
}
}

// 比較対象となるクラス
class User {
private int id;
private String name;

public User(int id, String name) { this.id = id; this.name = name; }
public String getName() { return name; }

// 重複判定のためにhashCodeとequalsのオーバーライドが必須
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}

5. 応用・注意点:現場で陥りやすい罠

実務で最も注意すべき点は、equals()とhashCode()の実装漏れです。これらを実装していない場合、Objectクラスのデフォルトの挙動(メモリ上のアドレス比較)が行われ、見た目が同じオブジェクトでも重複が除去されません。

また、Sequenced Collections(Java 21以降)を利用する場合、Listの順序を保持したまま一意な要素を取得したいケースが多いですが、distinct()は順序を維持する性質(stable)があるため、安心して利用可能です。もし、巨大なデータセットを扱う場合は、メモリ消費量に注意してください。distinct()内部では、過去に出現した要素を保持するためにSetが内部的に生成されるため、メモリを大量に消費する可能性があります。大量データを扱う際は、可能な限り事前にフィルタリングを行うか、並列処理(parallelStream)のオーバーヘッドを考慮して設計することをお勧めします。

コメント

タイトルとURLをコピーしました