简体   繁体   English

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

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

I need to convert big amounts of java.util.Date objects to String, for now I am using我需要将大量 java.util.Date 对象转换为 String,现在我正在使用

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

but it requires create a new SimpleDateFormat object each time.但它每次都需要创建一个新的 SimpleDateFormat 对象。

What is the fastest way to parse a String in needed format in a big amounts without creating new SimpleDateFormat objects, for example yyyy-MM-dd pattern from java.util.Date?在不创建新的 SimpleDateFormat 对象(例如 java.util.Date 中的 yyyy-MM-dd 模式)的情况下,以所需格式大量解析字符串的最快方法是什么?

I am using java 7. Solutions from java 8 are not acceptable for me我正在使用 java 7。java 8 的解决方案对我来说是不可接受的

tl;dr tl;博士

The back-port of the java.time classes takes under a microsecond to generate a String from a LocalDate in your desired pattern. java.time类的后向端口花费不到一微秒的时间从所需模式的LocalDate生成String

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

Using this library, I would not worry about date-time strings being a bottleneck.使用这个库,我不会担心日期时间字符串会成为瓶颈。

ThreeTen-Backport三十向后移植

The modern approach uses the java.time classes that supplanted the terrible old date-time classes such as Date & Calendar .现代方法使用java.time类取代了糟糕的旧日期时间类,例如Date & Calendar For Java 6 & 7, most of that functionality is back-ported in the ThreeTen-Backport project, using nearly identical API.对于 Java 6 和 7,大部分功能在ThreeTen-Backport项目中反向移植,使用几乎相同的 API。 Add the library and import: import org.threeten.bp.*;添加库并导入: import org.threeten.bp.*;

Your example format of YYYY-MM-DD is the default used by the LocalDate class when parsing/generating text.您的 YYYY-MM-DD 示例格式是LocalDate类在解析/生成文本时使用的默认格式。

Example code.示例代码。

Set up a list of LocalDate objects.设置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 );
}

Run the test.运行测试。

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 );

Results: Under a microsecond each结果:每个不到一微秒

When running on a MacBook Pro (Retina, 15-inch, Late 2013), 2.3 GHz Intel Core i7, 16 GB 1600 MHz DDR3, in IntelliJ 2018.3, using Java 10.0.2 from the OpenJDK -based Zulu JVM from Azul Systems…在 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……

When running a batch of 100 years, I get about 650 nanoseconds each.当运行一批 100 年时,我每次得到大约 650 纳秒。 That is about two/thirds of a microsecond.那是大约三分之二微秒。

When running a batch of 1,000 years, I get about 260 nanoseconds each.当运行一批 1,000 年时,我每次得到大约 260 纳秒。 That is about a quarter of a microsecond.这大约是四分之一微秒。

I doubt processing date strings with this library will prove to be a bottleneck in your app's performance.我怀疑使用此库处理日期字符串是否会成为应用程序性能的瓶颈。

Thread-safety线程安全

The java.time classes are designed to be inherently thread-safe , including the use of immutable objects . java.time类被设计为本质上是线程安全的,包括使用不可变对象

You can cache a single DateTimeFormatter object, and use it repeatedly, even across threads.您可以缓存单个DateTimeFormatter对象,并重复使用它,甚至可以跨线程使用。

Your desired format, defined by the ISO 8601 standard, is pre-defined as a constant in both java.time and in the ThreeTen-Backport library: DateTimeFormatter .ISO_LOCAL_DATE .您所需的格式由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 ) ;

About java.time关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于 Java 8 及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar ,和SimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial .要了解更多信息,请参阅Oracle 教程 And search Stack Overflow for many examples and explanations.并在 Stack Overflow 上搜索许多示例和解释。 Specification is JSR 310 .规范是JSR 310

You may exchange java.time objects directly with your database.您可以直接与您的数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later.使用符合JDBC 4.2或更高版本的JDBC 驱动程序 No need for strings, no need for java.sql.* classes.不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes?从哪里获得 java.time 类?

To convert many dates into string, you can use same SimpleDateFormat , but do it only in one thread, because SimpleDateFormat is not thread safe.要将多个日期转换为字符串,您可以使用相同的SimpleDateFormat ,但只能在一个线程中执行,因为SimpleDateFormat不是线程安全的。 But, as one of possible variant, you can create util class and hold SimpleDateFormat in ThreadLocal variable and use it anywhere.但是,作为可能的变体之一,您可以创建 util 类并在ThreadLocal变量中保存SimpleDateFormat并在任何地方使用它。

Slow: SimpleDateFormat is been created multiple times:慢: SimpleDateFormat被创建多次:

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

Fast: SimpleDateFormat is been created only once and use multiple times快速: SimpleDateFormat只创建一次并多次使用

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

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

Faster: SimpleDateFormat is been declared once per thread, date list is been formatted with multiple threads (eg 10 threads with 10% of all dates each):更快:每个线程声明一次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())

parsing&formating are compute-intensive tasks, if your computer has multiple processors, I would suggest to make your task multi-threading.解析和格式化是计算密集型任务,如果您的计算机有多个处理器,我建议您将任务设为多线程。

For exmaple:例如:

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

a quicker way would be not to re-create the SimpleDateFormat each time一种更快的方法是不要每次都重新创建 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