从java.util.Date解析String的最快方法

时间:2018-10-16 08:37:51

标签: java date

我需要将大量java.util.Date对象转换为String, 现在我正在使用

String dateString = new SimpleDateFormat(dateFormat).format(value);

,但是每次都需要创建一个新的SimpleDateFormat对象。

以大格式解析所需格式的字符串的最快方法是什么  数量而不创建新的SimpleDateFormat对象,例如java.util.Date中的yyyy-MM-dd模式?

我正在使用Java7。Java8的解决方案对我来说不可接受

4 个答案:

答案 0 :(得分:2)

tl; dr

java.time 类的后端口要花费一毫秒以内,以您期望的方式从String生成LocalDate

String output = myLocalDate.toString() ;  // Takes less than a microsecond.

使用该库,我不必担心日期时间字符串会成为瓶颈。

ThreeTen-Backport

现代方法使用 java.time 类代替了可怕的旧日期时间类,例如DateCalendar。对于Java 6和7,大多数功能都是使用几乎相同的API在ThreeTen-Backport项目中向后移植的。添加库并导入:import org.threeten.bp.*;

您的示例格式YYYY-MM-DD是LocalDate类在解析/生成文本时使用的默认格式。

示例代码。

设置LocalDate个对象的列表。

long years = 1000;
LocalDate today = LocalDate.now();
LocalDate lastDate = today.plusYears( years );
int initialCapacity = ( int ) ( ( years + 1 ) * 366 );
List < LocalDate > dates = new ArrayList <>( initialCapacity );
LocalDate localDate = today;
System.out.println( "From: " + today + " to: " + lastDate );
while ( localDate.isBefore( lastDate ) ) {
    dates.add( localDate );
    // Setup next loop.
    localDate = localDate.plusDays( 1 );
}

运行测试。

long start = System.nanoTime();

for ( LocalDate date : dates ) {
    String output = date.toString(); // Generate text in standard ISO 8601 format.
}

long stop = System.nanoTime();
long elapsed = ( stop - start );
long nanosEach = elapsed / dates.size();

System.out.println( "nanosEach: " + nanosEach );

结果:每次不到一微秒

当在IntelliJ 2018.3中使用基于OpenJDK的Java 10.0.2在MacBook Pro(Retina,15英寸,2013年末),2.3 GHz Intel Core i7、16 GB 1600 MHz DDR3上运行时, Zulu JVM来自Azul Systems…

  

运行100年后,每个时间大约为650纳秒。那大约是微秒的三分之二。

     

运行1000年后,每个时间大约为260纳秒。那大约是四分之一秒。

我怀疑使用此库处理日期字符串是否会成为应用程序性能的瓶颈。

线程安全

java.time 类被设计为固有的thread-safe,包括使用immutable objects

您可以缓存单个DateTimeFormatter对象,甚至在线程之间也可以重复使用。

ISO 8601标准定义的所需格式在 java.time ThreeTen-Backport 库中均已预定义为常量。 :DateTimeFormatter .ISO_LOCAL_DATE

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd" ) ;  // Or just use the pre-defined constant for that particular pattern, `DateTimeFormatter .ISO_LOCAL_DATE`, also used by default in `LocalDate::toString`.
…
String output = localDate.format( f ) ;

关于 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类?

答案 1 :(得分:1)

一种更快的方法是不要每次都重新创建SimpleDateFormat

SimpleDateFormat df = new SimpleDateFormat(dateFormat);  // outside loop or a field 
....
String dateString = df.format(value);

答案 2 :(得分:1)

要将许多日期转换为字符串,可以使用相同的SimpleDateFormat,但只能在一个线程中执行,因为SimpleDateFormat并不是线程安全的。但是,作为一种可能的变体,您可以创建util类并将SimpleDateFormat保留在ThreadLocal变量中,并在任何地方使用它。

慢: SimpleDateFormat被多次创建:

for(Date date : hugeDateList)
    String str = new SimpleDateFormat("yyyy-MM-dd").format(date);

快速SimpleDateFormat仅创建一次并使用多次

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

for(Date date : hugeDateList)
    String str = df.format(date);

更快::每个线程一次声明SimpleDateFormat,使用多个线程格式化日期列表(例如10个线程,每个线程占所有日期的10%):

public final class TimeUtils {

    private static final ThreadLocal<DateFormat> THREAD_LOCAL_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

    public static String format(Date date) {
        return date != null ? THREAD_LOCAL_DATE_FORMAT.get().format(date) : null;
    }

}

// TODO this is Java8 only for example, in Java7 ther're lines to create and run threads (not related to the question)
hugeDateList.parallelStream().map(TimeUtils::format).collect(Collectors.toList())

答案 3 :(得分:1)

解析和格式化是计算密集型任务,如果您的计算机具有多个处理器,我建议您将任务设置为多线程。

例如:

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class MyCallable implements Callable<String> {

    Date date;

    public MyCallable(Date date) {
        this.date = date;
    }

    @Override
    public String call() throws Exception {
        return new SimpleDateFormat(your pattern).format(date);
    }
}

public class Example {

    public static void main(String[] args) {

        // thread pool
        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        // your date objects
        List<Date> dates = ...

        List<Future<String>> futureList = new ArrayList<>();

        // let the thread poll run tasks
        for (Date date : dates) {
            MyCallable myCallable = new MyCallable(date);
            futureList.add(executorService.submit(myCallable));
        }

        // collect result
        List<String> results = new ArrayList<>();

        for (Future<String> future : futureList) {
            try {
                results.add(future.get());
            } catch (Exception e) {
                // 
            }
        }
    }
}