我正在读取包含日期的文本文件,我想在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....
}
日期和时区的转换是昂贵的。如果您可以假设您的日期/时间彼此相似,则可以在每分钟更改时转换日期和小时/分钟(或仅在您使用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(":", "")));
}
我建议编写一个自定义解析器,它会更快。就像是:
Date parseYYYYMMDDHHMM(String strDate) {
String yearString = strDate.substring(0, 4);
int year = Integer.parseInt(yearString);
...
另一种方法是使用datetime(w / o millis)的预先计算的hashmap来进行unix-timestamp。如果没有太多不同的日期将会工作(或者您可以在日期翻转后重新计算它)。
String
对LocalDateTime
和DateTimeFormatter
进行的每次解析都需要不到1,500纳秒(0.0000015秒)。您正在使用现在遗留的麻烦的旧日期时间类,取而代之的是java.time类。
让我们做一些微基准测试,看看在java.time中解析日期时间字符串的速度有多慢。
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纳米。
因此,在配备四核Intel i7 CPU的MacBook Pro笔记本电脑上解析一百万个此类输入需要大约一秒半的时间。在我的测试运行中,每个解析大约需要1,000到1,500纳秒。
在我看来,这不是性能问题。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,如java.util.Date
,Calendar
和SimpleDateFormat
。
现在在Joda-Time的maintenance 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的试验场。你可能会在这里找到一些有用的类,如Interval
,YearWeek
,YearQuarter
和more。