Java最快的OffsetDateTime解析器

时间:2019-02-07 20:06:05

标签: java datetime java-time datetime-parsing

用Java将日期/时间解析为OffsetDateTimes的最快方法是什么?是否有比标准库更快的库?

例如

OffsetDatetime x = Something.parse("2018-01-02T12:34:56+00:00");

2 个答案:

答案 0 :(得分:2)

据我所知,答案是否定的,没有其他库可以将2018-01-02T12:34:56+00:00"(ISO 8601格式)之类的字符串解析为OffsetDateTime对象。如果有的话,我本来希望听到或读到它。

接下来的一点让我感到无所适从,但是我也对标准库(AKA java.time)足够有效率,并且可能达到预期的速度感到印象深刻。

编辑:我很好奇并编写了自己的解析方法,以查看我是否能够胜过单参OffsetDateTime.parse。我曾是。我自己的方法(请参见下面的信息)没有内置方法的灵活性,它只接受大量标准格式的变体中的一个,这可能是其性能的强项。解析一百万次字符串需要花费时间:

    使用OffsetDateTime.parse
  • 1.034秒 使用我自己的方法
  • 0.117秒

这不是推荐!我可能永远不会使用自己的方法。对于绝大多数目的而言,维护负担是不值得的。如果有一天有另一种ISO 8601变体出现,那么您将遇到昂贵的支持问题和错误修复。

我的方法很简单:

private static final OffsetDateTime parse(String s) {
    char offsetSign;
    if (s.length() != 25
            || s.charAt(4) != '-'
            || s.charAt(7) != '-'
            || s.charAt(10) != 'T'
            || s.charAt(13) != ':'
            || s.charAt(16) != ':'
            || ((offsetSign = s.charAt(19)) != '+' && offsetSign != '-')
            || s.charAt(22) != ':') {
        throw new IllegalArgumentException();
    }
    int offsetHours = Integer.parseInt(s.substring(20, 22));
    int offsetMinutes = Integer.parseInt(s.substring(23, 25));
    if (offsetSign == '-') {
        offsetHours = -offsetHours;
        offsetMinutes = -offsetMinutes;
    }
    return OffsetDateTime.of(Integer.parseInt(s.substring(0, 4)),
            Integer.parseInt(s.substring(5, 7)),
            Integer.parseInt(s.substring(8, 10)), 
            Integer.parseInt(s.substring(11, 13)), 
            Integer.parseInt(s.substring(14, 16)), 
            Integer.parseInt(s.substring(17, 19)), 
            0,
            ZoneOffset.ofHoursMinutes(offsetHours, offsetMinutes));
}

我知道代码中的错误。我认为不值得彻底测试并修复此答案的错误,因为它不太可能对性能产生太大影响。

答案 1 :(得分:1)

tl; dr

每个解析时间不到一毫秒,您不必担心通过OffsetDateTime优化解析。当然,您必须要炸更大的鱼。

详细信息

让我们尝试一些基准测试。

注意事项:众所周知,微基准测试不可靠。但是希望这可以使我们接近现实的理解。

注意::我赶紧发送此代码和此帖子。请仔细检查我的工作。

为避免JVM进行运行时优化,我进行了一些微不足道的尝试,我使用31个不同的值,其中1月的每一天都使用一个值。我将这些重复一千遍以得到31,000个列表。然后我重新整理了清单。

尽管如此,我的结果表明在运行时需要进行大量优化。每次解析的纳秒级数*随循环数而变化很大。

  • 100_000个循环=每个解析1573纳秒(1微秒)
  • 10_000 = 4,243
  • 1_000 = 10,177
  • 100 = 31,125
  • 1 =每个解析度693,687纳米。 (693微秒,超过半毫秒)。

我使用了Azul Systems的Java 11 JVM Zulu 产品,该产品基于OpenJDK版本11.0.2。在MacBook Pro(Retina,15英寸,2013年末),2.3 GHz Intel Core i7、16 GB 1600 MHz DDR3上运行。

结果摘要:

  • 对于每种解析,最坏的情况是少于millisecond
  • 最好的情况是每个microsecond只有一个。

我的结论:

  • 不要担心解析您的OffsetDateTime输入字符串。
  • 您可能会陷入premature optimization的陷阱。

代码。

System.out.println( "INFO - Starting the OffsetDateTime parsing benchmark." );

List < String > inputsShort = new ArrayList <>( 31 );
inputsShort.add( "2018-01-01T12:34:56+00:00" );
inputsShort.add( "2018-01-02T12:34:56+00:00" );
inputsShort.add( "2018-01-03T12:34:56+00:00" );
inputsShort.add( "2018-01-04T12:34:56+00:00" );
inputsShort.add( "2018-01-05T12:34:56+00:00" );
inputsShort.add( "2018-01-06T12:34:56+00:00" );
inputsShort.add( "2018-01-07T12:34:56+00:00" );
inputsShort.add( "2018-01-08T12:34:56+00:00" );
inputsShort.add( "2018-01-09T12:34:56+00:00" );
inputsShort.add( "2018-01-10T12:34:56+00:00" );
inputsShort.add( "2018-01-11T12:34:56+00:00" );
inputsShort.add( "2018-01-12T12:34:56+00:00" );
inputsShort.add( "2018-01-13T12:34:56+00:00" );
inputsShort.add( "2018-01-14T12:34:56+00:00" );
inputsShort.add( "2018-01-15T12:34:56+00:00" );
inputsShort.add( "2018-01-16T12:34:56+00:00" );
inputsShort.add( "2018-01-17T12:34:56+00:00" );
inputsShort.add( "2018-01-18T12:34:56+00:00" );
inputsShort.add( "2018-01-19T12:34:56+00:00" );
inputsShort.add( "2018-01-20T12:34:56+00:00" );
inputsShort.add( "2018-01-21T12:34:56+00:00" );
inputsShort.add( "2018-01-22T12:34:56+00:00" );
inputsShort.add( "2018-01-23T12:34:56+00:00" );
inputsShort.add( "2018-01-24T12:34:56+00:00" );
inputsShort.add( "2018-01-25T12:34:56+00:00" );
inputsShort.add( "2018-01-26T12:34:56+00:00" );
inputsShort.add( "2018-01-27T12:34:56+00:00" );
inputsShort.add( "2018-01-28T12:34:56+00:00" );
inputsShort.add( "2018-01-29T12:34:56+00:00" );
inputsShort.add( "2018-01-30T12:34:56+00:00" );
inputsShort.add( "2018-01-31T12:34:56+00:00" );

int loops = 100; // 100_000=1,573 nanos each parse. 10_000=4,243. 1_000=10,177. 100=31,125. 1=693,687 nanos each parse.
List < String > inputs = new ArrayList <>( inputsShort.size() * loops );
for ( int i = 1 ; i <= loops ; i++ ) {
    inputs.addAll( inputsShort );
}
Collections.shuffle( inputs );
//System.out.println( inputs );

long start = System.nanoTime();
for ( String input : inputs ) {
    OffsetDateTime odt = OffsetDateTime.parse( input );
}
long stop = System.nanoTime();
long nanosPerParse = ( ( stop - start ) / inputs.size() );
System.out.println( "INFO: nanosPerParse: " + nanosPerParse + " for a count of: " + inputs.size() + "." );