JavaのStreamのdistinct()関数は、重複を削除するためのものです。
JavaのStreamのdistinct()メソッドは、異なる要素の新しいStreamを返します。これは、重複した要素を処理する前にコレクションから除去するのに役立ちます。
JavaのStreamのdistinct()メソッドをパラフレーズすると次のようになります:「JavaのStreamのdistinct()メソッド」
- The elements are compared using the equals() method. So it’s necessary that the stream elements have proper implementation of equals() method.
- If the stream is ordered, the encounter order is preserved. It means that the element occurring first will be present in the distinct elements stream.
- If the stream is unordered, then the resulting stream elements can be in any order.
- Stream distinct() is a stateful intermediate operation.
- Using distinct() with an ordered parallel stream can have poor performance because of significant buffering overhead. In that case, go with sequential stream processing.
重複する要素をdistinct()を使って削除する。
コレクションから重複した要素を削除するために、stream distinct() メソッドをどのように使用するか見てみましょう。
jshell> List<Integer> list = List.of(1, 2, 3, 4, 3, 2, 1);
list ==> [1, 2, 3, 4, 3, 2, 1]
jshell> List<Integer> distinctInts = list.stream().distinct().collect(Collectors.toList());
distinctInts ==> [1, 2, 3, 4]
Streamのdistinct()メソッドとforEach()メソッドを使用して、重複要素を除外して処理する。
distinct()は中間操作なので、forEach()メソッドと組み合わせてそれだけユニークな要素を処理することができます。
jshell> List<Integer> list = List.of(1, 2, 3, 4, 3, 2, 1);
list ==> [1, 2, 3, 4, 3, 2, 1]
jshell> list.stream().distinct().forEach(x -> System.out.println("Processing " + x));
Processing 1
Processing 2
Processing 3
Processing 4
カスタムオブジェクトでdistinct()を使う。
distinct()を使用してリストから重複した要素を削除するシンプルな例を見てみましょう。
package com.scdev.java;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class JavaStreamDistinct {
public static void main(String[] args) {
List<Data> dataList = new ArrayList<>();
dataList.add(new Data(10));
dataList.add(new Data(20));
dataList.add(new Data(10));
dataList.add(new Data(20));
System.out.println("Data List = "+dataList);
List<Data> uniqueDataList = dataList.stream().distinct().collect(Collectors.toList());
System.out.println("Unique Data List = "+uniqueDataList);
}
}
class Data {
private int id;
Data(int i) {
this.setId(i);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return String.format("Data[%d]", this.id);
}
}
結果:
Data List = [Data[10], Data[20], Data[10], Data[20]]
Unique Data List = [Data[10], Data[20], Data[10], Data[20]]
distinct()メソッドは、重複する要素を削除しませんでした。これは、Dataクラスにequals()メソッドを実装していなかったためです。したがって、スーパークラスのObjectのequals()メソッドが同じ要素を識別するために使用されました。Objectクラスのequals()メソッドの実装は以下の通りです。
public boolean equals(Object obj) {
return (this == obj);
}
同じIDを持つデータオブジェクトでも、参照しているオブジェクトが異なる場合、それらは等しくないと見なされます。そのため、カスタムオブジェクトを使用してストリームのdistinct()メソッドを使う場合は、equals()メソッドを実装することが非常に重要です。また、CollectionクラスのAPIでは、equals()メソッドとhashCode()メソッドの両方が使用されてオブジェクトの等値性の確認に使用されるため、両方の実装を提供することが良いでしょう。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
System.out.println("Data equals method");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Data other = (Data) obj;
if (id != other.id)
return false;
return true;
}
ヒント:「Eclipse > ソース > equals() と hashCode() を生成」メニューオプションを使用して、equals() と hashCode() メソッドを簡単に生成できます。equals() と hashCode() を実装した後の出力は次の通りです。
Data List = [Data[10], Data[20], Data[10], Data[20]]
Data equals method
Data equals method
Unique Data List = [Data[10], Data[20
参照: ストリームの distinct() メソッドのドキュメント