簡體   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