Help us understand the problem. What is going on with this article?

DateTimeFormatterとSimpleDateFormatの速度比較

More than 3 years have passed since last update.

DateTimeFormatterとSimpleDateFormatの速度比較

java8で導入されたDateTimeFormatterはスレッドセーフで、SimpleDateFormatと比べてインスタンス生成の分、速度が速いということなのでベンチマークをとってみる。

DateTimeFormatterはスレッドセーフ

実際に測る前にDateTimeFormatterはスレッドセーフなのか、ということを確認しておく。

まずは SimpleDateFormatがスレッドセーフでないことの確認。

現在日時に対し、0から指定回数を年月日時分秒それぞれに足したものを文字列に変換する処理を並行で実行し、出力する。
SimpleDateFormatをクラス変数として定義しているのに注意。

DateFormatterTest
public class DateFormatterTest {

    class SimpleDateFormatThreadTest {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        public void exec(int count) {
            IntStream.range(0, count)
            .mapToObj(i -> {
                Calendar cal = Calendar.getInstance();
                cal.add(Calendar.YEAR, i);
                cal.add(Calendar.MONTH, i);
                cal.add(Calendar.DATE, i);
                cal.add(Calendar.HOUR_OF_DAY, i);
                cal.add(Calendar.MINUTE, i);
                cal.add(Calendar.SECOND, i);
                return cal.getTime();
            })
            .map(d -> d + ":" + sdf.format(d))
            .parallel()
            .forEach(System.out::println);
        }
    }

    public static void main(String[] args) {
        new DateFormatterTest().go();
    }

    public void go() {
        int count = 20;
        new SimpleDateFormatThreadTest().exec(count);
    }
}

結果は以下のように、Dateを単純に出力したものと変換後が異なることがある。

Wed Aug 21 10:01:06 JST 2030:2030-08-21 10:01:06 // 正しい
Tue Aug 08 21:48:54 JST 2017:2017-08-21 10:01:06 // 不正

再現しない場合は何度か実行するか、countを調整してみる。

次にDateTimeFoematterの場合

DateFormatterTest
public class DateFormatterTest {

    class DateTimeFormatterThreadTest {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

        public void exec(int count) {
            IntStream.range(0, count)
            .mapToObj(i -> LocalDateTime.now()
                .plusYears(i)
                .plusMonths(i)
                .plusDays(i)
                .plusHours(i)
                .plusMinutes(i)
                .plusSeconds(i)
            )
            .map(d -> d + ":" + df.format(d))
            .parallel()
            .forEach(System.out::println);
        }
    }

    public static void main(String[] args) {
        new DateFormatterTest().go();
    }

    public void go() {
        int count = 20;
        new DateTimeFormatterThreadTest().exec(count);
    }
}

結果はこんな感じ。何度やっても正しく変換されているはずである。

2029-07-20T09:08:12.865:2029-07-20 09:08:12
2030-08-21T10:09:13.867:2030-08-21 10:09:13

もちろんこれでスレッドセーフであることの証明にはならないが、大丈夫そう。

速度測定

SimpleDateFormatがスレッドセーフでないため毎回インスタンスを生成しなければならないので、その時点で比較するまでもないが、一応確認する。

まずは速度比較のための基底クラス。
repeatの分だけrun()を繰り返してforEach。ただし何もしない。
各変換処理はこのクラスを継承する。

DateFormatterTest
    abstract class SpeedTest {
        LocalDateTime start;
        void start() {
            start = LocalDateTime.now();
        }
        void end() {
            LocalDateTime end = LocalDateTime.now();
            Duration d = Duration.between(start, end);
            System.out.println(getClass().getName() + " time:" + d);
        }
        void exec(int repeat) {
            start();
            IntStream.range(0, repeat)
            .mapToObj(run())
            .forEach(x -> {});
            end();
        }
        abstract IntFunction<String> run();
    }

以下、SimpleDateFormat, DateTimeFormatter, ついでにString.format()を使った変換処理。

DateFormatterTest
    class SimpleDateFormatSpeedTest extends SpeedTest {
        @Override
        IntFunction<String> run() {
            return i -> {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return sdf.format(new Date());
            };
        }
    }

    class DateTimeFormatterSpeedTest extends SpeedTest {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"); // 年はuuuuであることに注意
        @Override
        IntFunction<String> run() {
            return i -> df.format(LocalDateTime.now());
        }
    }

    class StringFormatterSpeedTest extends SpeedTest {
        @Override
        IntFunction<String> run() {
            return i -> String.format("%tF %<tT", new Date());
        }
    }

最後に実行するところ。

DateFormatterTest
    public void go() {

        int repeat = 100;
        new SimpleDateFormatSpeedTest().exec(repeat);
        new DateTimeFormatterSpeedTest().exec(repeat);
        new StringFormatterSpeedTest().exec(repeat);
    }

実行結果。
やはり、DateTimeFormatterが速いが、String.formatも健闘している。

DateFormatterTest$SimpleDateFormatSpeedTest time:PT0.168S
DateFormatterTest$DateTimeFormatterSpeedTest time:PT0.016S
DateFormatterTest$StringFormatterSpeedTest time:PT0.022S

結論

DateTimeFormatterを使おう。

今回のソースはこちら

https://github.com/tiibun/DateFormatterTest/blob/master/src/DateFormatterTest.java

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away