こんにちは、さるまりんです。
Javaで List を使っていると、
「同じ値が何個も入っているな」
ということがあります。
例えば、こんなリストがあったとします。
List<String> animals = List.of(
"monkey",
"cat",
"monkey",
"dog",
"cat"
);
この中には、
monkey
cat
が、それぞれ2回入っています。
これを、
monkey
cat
dog
のように、一つずつにしたい。
そんなとき、JavaのStreamには、
distinct()
というものがあります。
今回は、distinct() を使って、Listの重複を取り除いてみます。
まずは重複したListを用意します
こんなListを作ってみます。
List<String> animals = List.of(
"monkey",
"cat",
"monkey",
"dog",
"cat"
);
そのまま表示してみます。
System.out.println(animals);
結果は、
[monkey, cat, monkey, dog, cat]
です。
monkey と cat が重複していますね。
今回は、この重複を取り除きます。
distinct()を使ってみます
Streamを使うと、こんなふうに書けます。
List<String> uniqueAnimals = animals.stream()
.distinct()
.toList();
表示してみます。
System.out.println(uniqueAnimals);
結果は、
[monkey, cat, dog]
となります。
重複していた、
monkey
cat
が一つずつになりました。
これが、
distinct()
です。
distinctってどういう意味?
英語の distinct には、
異なる
別々の
といった意味があります。
JavaのStreamで使う distinct() は、
重複しているものを一つにする
ための処理です。
今回なら、
monkey
cat
monkey
dog
cat
というデータを、
monkey
cat
dog
にしてくれています。
同じものが何度出てきても、一つだけ残るんですね。
Streamの流れで見てみましょう
以前の記事、JavaのStreamってなに?for文と比べながらコードを読んでみようでも、Streamについて見てみました。
Streamは、処理を上から順番に読んでいくとわかりやすいのでしたね。
今回のコードも見てみます。
List<String> uniqueAnimals = animals.stream()
.distinct()
.toList();
まず、
animals.stream()
で、animals をStreamとして扱います。
そして、
.distinct()
で、重複を取り除きます。
最後に、
.toList();
で、結果をListとして受け取っています。
なので、
animals
↓
Streamにする
↓
重複を取り除く
↓
Listにする
という流れです。
こうして見ると、何をしているのかわかりやすいですね。
元のListはどうなる?
ここで一つ気になることがあります。
List<String> uniqueAnimals = animals.stream()
.distinct()
.toList();
としたとき、
元の、
animals
はどうなるのでしょうか。
確認してみます。
System.out.println(animals);
System.out.println(uniqueAnimals);
結果は、
[monkey, cat, monkey, dog, cat]
[monkey, cat, dog]
となります。
元の animals はそのままです。
distinct() が元のListを書き換えているわけではありません。
Streamで処理した結果を、
uniqueAnimals
という新しいListとして受け取っています。
なお、toList() で受け取ったListは変更できないListです。
あとから add() や remove() をしたい場合には、別のListに入れ直すなどの方法が必要になります。
順番はどうなる?
今回の結果は、
[monkey, cat, dog]
でした。
元のListは、
[monkey, cat, monkey, dog, cat]
です。
最初に出てきた、
monkey
cat
dog
の順番が残っています。
通常のListから作ったStreamのように、順序を持つStreamでは、distinct() は最初に現れた要素を残しながら重複を取り除きます。
なので今回の場合は、
monkey
cat
dog
という順番になります。
数字でも使えます
文字列だけではありません。
例えば、
List<Integer> numbers = List.of(
1,
2,
1,
3,
2,
4
);
というListがあるとします。
これも、
List<Integer> uniqueNumbers = numbers.stream()
.distinct()
.toList();
とすれば、
[1, 2, 3, 4]
になります。
StringでもIntegerでも、
同じものを一つにしたい
というときに使えるんですね。
自分で作ったクラスの場合は少し注意
StringやIntegerでは、ここまで見てきたように簡単に重複を取り除くことができます。
ただ、自分で作ったクラスのオブジェクトを distinct() で比べたい場合には、少し注意が必要です。
distinct() は、要素が同じかどうかを equals() を基準に判断します。
そのため、自分で作ったクラスで、
「何を同じものとして扱うのか」
を決めたい場合には、equals() の実装を考える必要があります。
また、equals() を実装する場合には、hashCode() との整合性も大切です。
record を使っている場合には、要素をもとに equals() や hashCode() が自動的に用意されるので、このあたりが扱いやすくなることもあります。
ここは少し話が広がるので、今回は、
StringやIntegerなら、まずはdistinct()で重複を取り除ける
というところまでにしておきましょう。
filter()やmap()と一緒にも使えます
Streamなので、ほかの処理と組み合わせることもできます。
例えば、
List<String> animals = List.of(
"monkey",
"cat",
"monkey",
"dog",
"cat"
);
から、4文字以上のものだけにして、さらに重複を取り除くなら、
List<String> result = animals.stream()
.filter(animal -> animal.length() >= 4)
.distinct()
.toList();
と書けます。
結果は、
[monkey]
です。
まず、
filter()
で条件に合うものだけを残して、
そのあと、
distinct()
で重複を取り除いています。
Streamは、こうやって処理をつなげていけるところも便利ですね。
まとめ
今回は、JavaのListから重複を取り除く方法を見てみました。
例えば、
List<String> animals = List.of(
"monkey",
"cat",
"monkey",
"dog",
"cat"
);
というListなら、
List<String> uniqueAnimals = animals.stream()
.distinct()
.toList();
とすることで、
[monkey, cat, dog]
にできます。
流れとしては、
stream()
↓
distinct()
↓
toList()
です。
distinct() は、
重複しているものを一つにする
ための処理です。
Streamには、filter() や map() のほかにも、こうした便利な処理があります。
「Listの中に同じものがいくつもあるな」
というときには、
distinct()
を思い出してみてください。
一つずつ使いながら覚えていくと、Streamも少しずつ身近になってくると思います。
読んでくださってありがとうございました。
それではまた!