4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaAdvent Calendar 2023

Day 18

結局どっちが速いの? for vs Stream

Last updated at Posted at 2023-12-17

はじめに

こんにちは、@rs_tukkiです。
これはJava Advent Calender 2023の18日目の記事です。

繰り返し処理と言えば

Javaで繰り返しと言えばいの一番に習うのはfor構文ですが、
Java8からは新たな繰り返しや集約処理の構文としてStreamがサポートされています。

これは処理の流れを宣言的に記述できる構文で、
たとえば、

for (int i = 1; i <= 5; i++) {
    System.out.println(i);
}

といった繰り返し処理は、

Stream.of(1, 2, 3, 4, 5).forEach(System.out::println);

といったように関数の繋ぎ合わせで記述することができます。

…ところで、このfor文とStream文って、どちらが速いんでしょうか。
感覚的には後から出てきたStreamの方が速そうですが、まずはGPTに相談してみました。

image.png

基本的にはデータ量が小さいほどfor文の方がパフォーマンスが向上する、とのこと。
本当にそうなのか、ちょっと検証してみることにします。

どちらが速いのか

という訳で、書いたコードがこちら。

ちなみにJavaのバージョンは17.0.4です。

    public static void main(String[] args) {
        final List<Integer> dataSet = new ArrayList<>();
        for (int i = 1; i <= 100000000; i++) {
            dataSet.add(i);
        }

        System.out.println("開始:for");
        long startTime = System.currentTimeMillis();
        long sumFor = 0;
        for (Integer data : dataSet) {
            if (data % 3 == 0) {
                sumFor += data;
            }
        }
        System.out.println(sumFor);
        long endTime = System.currentTimeMillis();
        System.out.println("終了:for");
        System.out.println("処理時間:" + (endTime - startTime) + "ms");

        System.out.println("===============");

        System.out.println("開始:stream");
        startTime = System.currentTimeMillis();
        final long sumStream = dataSet.parallelStream()
                .filter(entry -> entry % 3 == 0)
                .mapToLong(entry -> entry)
                .sum();
        System.out.println(sumStream);
        endTime = System.currentTimeMillis();
        System.out.println("終了:stream");
        System.out.println("処理時間:" + (endTime - startTime) + "ms");
    }

めちゃくちゃ単純ですが、やっていることは

  • 1から1億までの数値からなるListを用意し
  • List内の3の倍数だけを足し合わせて最後に出力する
  • forとStreamでそれぞれ行い実行時間を計測する

というプログラムです。
本当はもう少し大きい数でやりたかったのですが、int型の最大値までデータセットを用意するとOOMになってしまうので断念しました...

実行結果

そしてこちらのコードを実行してみると、以下のような結果になりました。

開始:for
1666666683333333
終了:for
処理時間:729ms
===============
開始:stream
1666666683333333
終了:stream
処理時間:504ms

何回か実行してみましたが、平均してStreamの方が1.4倍前後速くなっています。
GPTによるとデータ量によってはforの方が速くなるとのことなので、データセットをもう少し減らして再度実行してみると、

5000万

開始:for
416666658333333
終了:for
処理時間:246ms
===============
開始:stream
416666658333333
終了:stream
処理時間:159ms

5000万ではまだStreamが優位でしたが、

1000万

開始:for
16666668333333
終了:for
処理時間:80ms
===============
開始:stream
16666668333333
終了:stream
処理時間:87ms

1000万ではforが逆転しました。
とはいえここまで来るとほぼ差はなさそうなので、コード上でどちらを採用するか迷った場合

  • 巨大なデータセットを扱う処理はStream
  • データセットがそこまで多くない場合、実際に実行時間を計測して
    • Streamの方が速い場合はStream
    • forの方が速い場合、(数msの差が気にならないなら)可読性で判断

とするのがよさそうです。

まとめ

今回はforとStreamの実行時間を、実際にコードを書いて比較してみました。
最終的には場合によるという何とも言えない結果になりましたが、だからこそ何も考えずこちらと決めつけるのではなく、都度そのコードに対してどちらで書くのがよいかを考えるようにしてきましょう。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?