あいぼーいの部屋

@I_BOY_1204がJavaやJavaScriptに関して調べた事を忘れない様につらつらと記述する備忘録です。

Java SE8ではforEachよりもStramが早い!!

既に一週間以上経ち、かなり今更感がありますが、
JJUG ナイト・セミナー 「Java エンジニアのためのJava(再)入門」 6/20(木)開催 | 日本Javaユーザーグループ
に参加してきました。

その際に、@さんに勧められたのをきっかけに、私もJavaJavaScriptなど、自分の学んだことをブログにまとめようと思い、このページを作りました。
週に一回くらいは、何かしらの情報をアップしていこうと思います。
#このブログの使い方についても、少しずつマスターしていこう・・・

早速ですが、今日の話題は、JJUG ナイト・セミナー 「Java エンジニアのためのJava(再)入門」 6/20(木)開催 | 日本Javaユーザーグループで@さんからお話いただいた、from old Java to modern Javaの内容についてです。

内容的な部分は、最近Java7を使っている私としては、Java6の部分までは、特に知らないことろもなく、「ふんふん。そうだよねぇ。。。」と言う感じで聞いていました。
#いや、年齢的な意味でも、Java5でのジェネリクスの導入による衝撃なんかは知らないんだけどさ...^^;

知らないところが出てきたのは、Java7から。。。
以下、知らなかった点
Java SE7からObjects.HashというAPIができた
Java SE7では、Files.readAllLinesというAPIがある
の二点。(あれ?APIだけじゃね?話は文法的なところがメインじゃなかったっけ?)

そして、最も気になった部分があったのは、Java SE8の部分が終わって、質問タイムの時!

「新しい文法を古いものになれた人にどう進めればいいんでしょう?」

と言う質問に対する@さんの答えの中に、

「Streamを使うだけでforEachよりも格段に早いんです!」

って話がありました。

そんなことは全く知らない私は「え?そうなの?(ぽかーん」状態。。。

ということで、前置きが長くなりましたが、本日はここを検証します。

Streamをてっとり早く検証しようと思って、以下(ページ下部に準備)のようなソースコードを準備しました。
#ほぼ@さんがスライドにあげているもののパクリ^^;

実行した結果がこちら
for Loop Time is :5612
stream Loop Time is :3335
Parallel stream Loop Time is :4510
#あ、結果の単位はすべて[msec]です!

う。。。うん。確かに、Streamを使った方がforEachより早い!
並列化した方が遅いのは、処理内容が簡単すぎて、並列化のオーバヘッドの方が大きくなっただけだろうか?
ここは少し疑問に思うところ。。。時間があるときに、見てみようと思います。

というか、そもそも、なんでStreamを使うと早いかという部分も検証すべきなんですが、今週は忙しかったので、ごめんなさい。。。
ちゃんと調べておきます。

ということで、今回は、StreamがforEachよりも早いよ!と言うことを確認してみました。
実際、確かに早かったので、forEachではなく、Streamをじゃんじゃん使っていきましょう!(Lambda普及的な意味でも^^)
次回は、Java SE8で新しくなったJava VM(Nashorn)JavaScriptエンジン(Nashorn)に関して、書いてみようと思います。

(裏話)
実は、このソースコード、実際に動かしてみると、最初はStreamの方がかなり遅いという結果がでていました。
おかしいなぁ。。。と思いつつ、一日放置して、その後、ソースコードを書き直してみた結果がこれ。
うーん。。。たぶんどっかで書き間違えしたんだろうな。。。


//実行したコード
package com.iboy;

import com.iboy.entity.Record;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    public static int LIST_SIZE = 10000000;
    public static int SEED = 100000;

    /**
     * テストデータの作成
     * @return
     */
    private  List<Record> createTestData() {
        Random random = new Random(SEED);
        List<Record> list = new ArrayList<>();
        for (int i=0; i < LIST_SIZE; i++) {
            int tmp =  random.nextInt();
            Record tmpRecode = new Record(tmp);
            list.add(tmpRecode);
        }
        return list;
    }

    /**
     * 通常のforEachを用いた処理
     * @param data filter&sort対象のデータ
     */
    private void doForEachProcess(List<Record> data) {
        List<Record> list3 = new ArrayList<>(data);
        long currentTime = System.currentTimeMillis();
        List<Record> tmpList = new ArrayList<>();
        for (Record record : list3) {
            if (record.getScore() % 2 == 0) {
                tmpList.add(record);
            }
        }
        Comparator<Record> comparator = new Comparator<Record>() {
            @Override
            public int compare(Record o1, Record o2) {
                if (o1.getScore() < o2.getScore()) {
                    return -1;
                } else if (o1.getScore() == o2.getScore()) {
                    return 0;
                }
                return 1;
            }
        };
        Collections.sort(tmpList, comparator);

        long endTime = System.currentTimeMillis();
        System.out.println("for Loop Time is :" + (endTime - currentTime));
    }

    /**
     * Streamを用いた処理
     * @param data
     */
    private void doStreamProcess(List<Record> data) {
        List<Record> list = new ArrayList<>(data);
        long currentTime = System.currentTimeMillis();
        list.stream().filter(record -> record.getScore() % 2 == 0)
                .sorted((o1, o2) -> {
                    if (o1.getScore() < o2.getScore()) {
                        return -1;
                    } else if (o1.getScore() == o2.getScore()) {
                        return 0;
                    }
                    return 1;
                })
                .collect(Collectors.toList());

        long endStreamTime = System.currentTimeMillis();

        System.out.println("stream Loop Time is :" + (endStreamTime - currentTime));
    }

    /**
     * ParallelStreamを用いた処理
     * @param data
     */
    private void doParallelStreamProcess(List<Record> data) {
        List<Record> list = new ArrayList<>(data);
        long currentTime = System.currentTimeMillis();
        List<Record> sorted = list.parallelStream().filter(record -> record.getScore() % 2 == 0)
                .sorted((o1, o2) -> {
                    if (o1.getScore() < o2.getScore()) {
                        return -1;
                    } else if (o1.getScore() == o2.getScore()) {
                        return 0;
                    }
                    return 1;
                }).collect(Collectors.toList());

        long endParallelStreamTime = System.currentTimeMillis();
        System.out.println("Parallel stream Loop Time is :" + (endParallelStreamTime - currentTime));
    }

    public static void main(String[] args) {
        Main main = new Main();
        List<Record> list = main.createTestData();
        //Do forEach process
        main.doForEachProcess(list);
        //Do Stream process
        main.doStreamProcess(list);
        //Do parallel Stream process
        main.doParallelStreamProcess(list);
    }

}