简体   繁体   中英

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

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

but it requires create a new SimpleDateFormat object each time.

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?

I am using java 7. Solutions from java 8 are not acceptable for me

tl;dr

The back-port of the java.time classes takes under a microsecond to generate a String from a LocalDate in your desired pattern.

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 . For Java 6 & 7, most of that functionality is back-ported in the ThreeTen-Backport project, using nearly identical API. Add the library and import: import org.threeten.bp.*;

Your example format of YYYY-MM-DD is the default used by the LocalDate class when parsing/generating text.

Example code.

Set up a list of LocalDate objects.

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…

When running a batch of 100 years, I get about 650 nanoseconds each. That is about two/thirds of a microsecond.

When running a batch of 1,000 years, I get about 260 nanoseconds each. 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 .

You can cache a single DateTimeFormatter object, and use it repeatedly, even across threads.

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 .

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

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.

To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

To convert many dates into string, you can use same SimpleDateFormat , but do it only in one thread, because SimpleDateFormat is not thread safe. But, as one of possible variant, you can create util class and hold SimpleDateFormat in ThreadLocal variable and use it anywhere.

Slow: SimpleDateFormat is been created multiple times:

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

Fast: SimpleDateFormat is been created only once and use multiple times

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

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 df = new SimpleDateFormat(dateFormat);  // outside loop or a field 
....
String dateString = df.format(value);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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