Categories
Tags
AIML autoboxing AWS aws blockchain CTA data Databricks DataEngineering defer EOL equals findany findfirst GAS Google Drive header html html5 http ipc java Java javascript JSF named-pipe network nft nodejs PostgreSQL PySpark pyspark python Seasar2 serialize smart-contract spark sql stream Teeda TOCTOU UI UML unity catalog UX WCAG web3 Webデザイン word シーケンス図 システム設計 スイムレーン図 セキュリティ ダークパターン デッドロック トランザクション フローチャート レガシーシステム 外部設計 基本設計 設計 設計書 排他制御
1220 words
6 min
【Java Stream】findFirstとfindAnyの違いと使い分け(並列処理での注意点)
この記事は公開日から2年以上が経過しています。
【要約】findFirst vs findAny 比較
| メソッド | 返す要素 | 順序性の保証 | パフォーマンス(直列) | パフォーマンス(並列 parallelStream) |
|---|---|---|---|---|
findFirst() | 常に最初の要素 | 保証する | 同等 | 低い(順序維持のオーバーヘッド) |
findAny() | 任意の1要素 | 保証しない | 同等 | 高い(最初に見つかった要素を即返却) |
- 通常のストリーム (
stream()): 動作・速度ともにほぼ差はない。 - 並列ストリーム (
parallelStream()): 順序が不要であればfindAny()を使うことで処理性能が大幅に向上する。
あまり使うことのなかったfindFirstとfindAnyを調べたのでメモ
findFirstとは
findFirstメソッドは、Streamから最初の要素を返すために使用される。
順序付けされている状態であれば、findFirstは常に最初の要素を返す。
- 例
List<String> names = Arrays.asList("Test1", "Test2", "Test3", "Test4");
Optional<String> firstName = names.stream().findFirst();
firstName.ifPresent(System.out::println); //→Test1findAnyとは
findAnyメソッドは、ストリームから任意の要素を返す。
特に並列でストリームを回しているときに性能が良くなるらしい。。。
- 例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
Optional<String> anyName = names.parallelStream().findAny();
anyName.ifPresent(System.out::println);findAnyいつ使うの?
性能的に考えてもfindFirstで最初が返されるのであれば
感覚的にfindFirstの方が速そう、、、
なので試してみた。
前提
parallelStream自体がマルチコアプロセッサを活かして動く形なので以降の処理結果は
CPUに依存する。
XeonなどのマルチコアゴリゴリのCPU等であれば処理結果変わらない可能性がある。
parallelStreamでのfindFirstとfindAnyの比較
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
for(int i = 1; i < 11; i++){
execute(i);
}
}
private static void execute(int index){
System.out.println(index + "回目");
List<Integer> largeList = IntStream.range(0, 1000000).boxed().collect(Collectors.toList());
// findFirstの計測
long startTime1 = System.nanoTime();
Optional<Integer> first = largeList.parallelStream().findFirst();
long endTime1 = System.nanoTime();
System.out.println("findFirst took :" + (endTime1 - startTime1) + " ns");
// findAnyの計測
long startTime2 = System.nanoTime();
Optional<Integer> any = largeList.parallelStream().findAny();
long endTime2 = System.nanoTime();
System.out.println("findAny took :" + (endTime2 - startTime2) + " ns");
}
}1回目
findFirst took :533346 ns
findAny took :76237 ns
2回目
findFirst took :349193 ns
findAny took :70898 ns
3回目
findFirst took :163967 ns
findAny took :100547 ns
4回目
findFirst took :140695 ns
findAny took :106406 ns
5回目
findFirst took :146328 ns
findAny took :74622 ns
6回目
findFirst took :293930 ns
findAny took :95320 ns
7回目
findFirst took :141319 ns
findAny took :51707 ns
8回目
findFirst took :147466 ns
findAny took :63520 ns
9回目
findFirst took :138300 ns
findAny took :283056 ns
10回目
findFirst took :163080 ns
findAny took :91353 ns同じように要素を少なく(10個)にしてみた結果
1回目
findFirst took :132483 ns
findAny took :58254 ns
2回目
findFirst took :112732 ns
findAny took :53424 ns
3回目
findFirst took :108216 ns
findAny took :73001 ns
4回目
findFirst took :118181 ns
findAny took :52879 ns
5回目
findFirst took :117647 ns
findAny took :51617 ns
6回目
findFirst took :124880 ns
findAny took :63088 ns
7回目
findFirst took :126601 ns
findAny took :60481 ns
8回目
findFirst took :124372 ns
findAny took :59836 ns
9回目
findFirst took :120962 ns
findAny took :50714 ns
10回目
findFirst took :107710 ns
findAny took :51197 nsparallelStreamでのfindFirstとfindAnyの結果
結果的にはparallelStream同士ではfindFirstの方がかなり速かった。
parallelStreamとstreamの場合で比べてみたらどうなるのか気になったので
試してみた。
parallelStreamとStreamでのfindFirstとfindAnyの比較
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
for(int i = 1; i < 11; i++){
execute(i);
}
}
private static void execute(int index){
System.out.println(index + "回目");
List<Integer> largeList = IntStream.range(0, 100000).boxed().collect(Collectors.toList());
// findFirstの計測
long startTime1 = System.nanoTime();
Optional<Integer> first = largeList.stream().findFirst();
long endTime1 = System.nanoTime();
System.out.println("findFirst took :" + (endTime1 - startTime1) + " ns");
// findAnyの計測
long startTime2 = System.nanoTime();
Optional<Integer> any = largeList.parallelStream().findAny();
long endTime2 = System.nanoTime();
System.out.println("findAny took :" + (endTime2 - startTime2) + " ns");
}
}1回目
findFirst took :16188 ns
findAny took :85091 ns
2回目
findFirst took :15263 ns
findAny took :77307 ns
3回目
findFirst took :15275 ns
findAny took :108210 ns
4回目
findFirst took :11231 ns
findAny took :62859 ns
5回目
findFirst took :11406 ns
findAny took :60593 ns
6回目
findFirst took :11201 ns
findAny took :60128 ns
7回目
findFirst took :11290 ns
findAny took :59533 ns
8回目
findFirst took :17196 ns
findAny took :1116963 ns
9回目
findFirst took :15139 ns
findAny took :77320 ns
10回目
findFirst took :16299 ns
findAny took :80137 nsこちらも同じように要素を少なく(10個)してみた結果
1回目
findFirst took :11960 ns
findAny took :65212 ns
2回目
findFirst took :10200 ns
findAny took :61745 ns
3回目
findFirst took :8921 ns
findAny took :55540 ns
4回目
findFirst took :8800 ns
findAny took :53469 ns
5回目
findFirst took :8788 ns
findAny took :54545 ns
6回目
findFirst took :10299 ns
findAny took :54831 ns
7回目
findFirst took :9144 ns
findAny took :57409 ns
8回目
findFirst took :8523 ns
findAny took :52583 ns
9回目
findFirst took :8659 ns
findAny took :53152 ns
10回目
findFirst took :9056 ns
findAny took :58739 nsparallelStreamとstreamでのfindFirstとfindAnyの結果
結果的には、Streamにした状態のfindFirstが速かった。
まとめ
parallelStreamを使用する場合はfindAnystreamで良い場合は、findFirstの方がよさげ。
ただ、parallelStreamが必要なレベルの重たい処理を行ったことがないため、parallelStreamでfindFirstの方がよい場合もあるのかも
関連記事・トピックリンク
【Java Stream】findFirstとfindAnyの違いと使い分け(並列処理での注意点)
https://tech.storias-blog.com/blogs/java_findfirst_vs_findany/作者
Storia
公開日
2024-06-07