为什么在Java中将String解析为Date很慢?我们可以加速吗?

问题描述 投票:6回答:3

我正在读取包含日期的文本文件,我想在Java中将表示日期的字符串解析为Date对象。我注意到操作很慢。为什么?有没有办法加速呢?我的文件看起来像:

2012-05-02 12:08:06:950, secondColumn, thirdColumn
2012-05-02 12:08:07:530, secondColumn, thirdColumn
2012-05-02 12:08:08:610, secondColumn, thirdColumn

我正在逐行读取文件,然后我从每一行获取日期String,然后我使用Date将其解析为SimpleDateFormat对象,如下所示:

DataInputStream in = new DataInputStream(myFileInputStream);
BufferedReader  br = new BufferedReader(new InputStreamReader(in));
String strLine;

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
while ((strLine = br.readLine()) != null)
{
    ....Do things....
    Date myDateTime = (Date)formatter.parse(myDateString);
    ...Do things....
}
java date simpledateformat date-parsing
3个回答
6
投票

日期和时区的转换是昂贵的。如果您可以假设您的日期/时间彼此相似,则可以在每分钟更改时转换日期和小时/分钟(或仅在您使用GMT时的日期),并自行生成秒数。

这将每分钟调用一次parse。根据您的假设,您可以每小时或每天一次。

String pattern = "yyyy-MM-dd HH:mm";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
String lastTime = "";
long lastDate = 0;
while ((strLine = br.readLine()) != null) {
    String myDateString = strLine.split(", ")[0];
    if (!myDateString.startsWith(lastTime)) {
        lastTime = myDateString.substring(0, pattern.length());
        lastDate = formatter.parse(lastTime).getTime();
    }
    Date date = new Date(lastDate + Integer.parseInt(myDateString.substring(pattern.length() + 1).replace(":", "")));
}

1
投票

我建议编写一个自定义解析器,它会更快。就像是:

Date parseYYYYMMDDHHMM(String strDate) {
   String yearString = strDate.substring(0, 4);
   int year = Integer.parseInt(yearString);
   ...

另一种方法是使用datetime(w / o millis)的预先计算的hashmap来进行unix-timestamp。如果没有太多不同的日期将会工作(或者您可以在日期翻转后重新计算它)。


0
投票

tl;dr

  • 使用java.time而不是遗留类。
  • 使用StringLocalDateTimeDateTimeFormatter进行的每次解析都需要不到1,500纳秒(0.0000015秒)。

java.time

您正在使用现在遗留的麻烦的旧日期时间类,取而代之的是java.time类。

让我们做一些微基准测试,看看在java.time中解析日期时间字符串的速度有多慢。

ISO 8601

ISO 8601标准定义了用于文本表示日期时间值的合理实用格式。在解析/生成字符串时,java.time类默认使用这些标准格式。

使用这些标准格式而不是发明自己的格式,如问题中所示。

DateTimeFormatter

定义格式模式以匹配您的输入。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss:SSS" );

我们将每个这样的输入解析为LocalDateTime,因为您的输入缺少时区指示或从UTC偏移。请记住,这些值不代表片刻,不是时间轴上的一个点。要成为实际时刻需要区域/偏移的上下文。

String inputInitial = "2012-05-02 12:08:06:950" ;
LocalDateTime ldtInitial = LocalDateTime.parse( inputInitial , f );

让我们做一堆这样的输入。

int count = 1_000_000;
List < String > inputs = new ArrayList <>( count );

for ( int i = 0 ; i < count ; i++ )
{
    String s = ldtInitial.plusSeconds( i ).format( f );
    inputs.add( s );
}

测试线束。

long start = System.nanoTime();
for ( String input : inputs )
{
    LocalDateTime ldt = LocalDateTime.parse( input , f );
}
long stop = System.nanoTime();
long elapsed = ( stop - start );
long nanosPerParse = (elapsed / count ) ;
Duration d = Duration.ofNanos( elapsed );

转储到控制台。

System.out.println( "Parsing " + count + " strings to LocalDateTime took: " + d  + ". About " + nanosPerParse + " nanos each.");

将1000000个字符串解析为LocalDateTime:PT1.320778647S。每个约1320纳米。

Too slow?

因此,在配备四核Intel i7 CPU的MacBook Pro笔记本电脑上解析一百万个此类输入需要大约一秒半的时间。在我的测试运行中,每个解析大约需要1,000到1,500纳秒。

在我看来,这不是性能问题。


About java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,如java.util.DateCalendarSimpleDateFormat

现在在Joda-Timemaintenance mode项目建议迁移到java.time班。

要了解更多信息,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规格是JSR 310

您可以直接与数据库交换java.time对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。你可能会在这里找到一些有用的类,如IntervalYearWeekYearQuartermore

© www.soinside.com 2019 - 2024. All rights reserved.