繁体   English   中英

从 java.util.Date 解析字符串的最快方法

[英]Fastest way to parse String from java.util.Date

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

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

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

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

我正在使用 java 7。java 8 的解决方案对我来说是不可接受的

tl;博士

java.time类的后向端口花费不到一微秒的时间从所需模式的LocalDate生成String

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

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

三十向后移植

现代方法使用java.time类取代了糟糕的旧日期时间类,例如Date & Calendar 对于 Java 6 和 7,大部分功能在ThreeTen-Backport项目中反向移植,使用几乎相同的 API。 添加库并导入: 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 );

结果:每个不到一微秒

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

当运行一批 100 年时,我每次得到大约 650 纳秒。 那是大约三分之二微秒。

当运行一批 1,000 年时,我每次得到大约 260 纳秒。 这大约是四分之一微秒。

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

线程安全

java.time类被设计为本质上是线程安全的,包括使用不可变对象

您可以缓存单个DateTimeFormatter对象,并重复使用它,甚至可以跨线程使用。

您所需的格式由ISO 8601标准定义,在java.timeThreeTen-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 及更高版本中。 这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar ,和SimpleDateFormat

现在处于维护模式Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle 教程 并在 Stack Overflow 上搜索许多示例和解释。 规范是JSR 310

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

从哪里获得 java.time 类?

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

慢: 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())

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

例如:

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) {
                // 
            }
        }
    }
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM