Reputation: 19905
Given a method that needs to use the values of the current day/month/year on every invocation and which is invoked several (millions or more) times per second, is there any faster way of implementing that functionality, other than creating a Calendar
object for the current time on every invocation?
Using Calendar.getInstance()
or calendar.setTime()
(with time field recalculation) is overly expensive to do many times per second.
Standard Java solutions are preferable (e.g., over using third party libraries).
Upvotes: 1
Views: 648
Reputation: 339053
is invoked several (millions or more) times per second
Really? If you truly are in such an extreme situation, and you have proven a bottleneck, then you could cache the current date, while testing a specified tolerance for whatever length of time you are willing for the date to be stale and possibly incorrect.
In defining a tolerance, use Duration
class to help with the math, making your code more readable.
final long tolerance = Duration.ofSeconds( 3 ).toNanos() ;
For maximum performance, use System.nanoTime()
to track elapsed nanoseconds since some arbitrary point in time. Be clear that this feature is not tracking time-of-day nor the calendar. We simply need to determine elapsed time since we last updated our cached date.
Record the current count of nanos every time we get the date.
long nanos = System.nanoTime() ;
Each time we need the date, compare the current value returned by System.nanoTime()
to that stored long
. If the difference exceeds our tolerance, fetch the date. If not exceeding, then return cached date.
if ( ( System.nanoTime() - nanos ) > tolerance )
{
cachedDate = LocalDate.now( z ) ;
}
return cachedDate ;
Caveat: This approach is only appropriate in the most extreme situation such as that described in the Question. For the other 99.999% of apps, just use LocalDate.now
, with no funny business. Do not fall into the trap of premature optimization.
Example code. This is first-draft, not reviewed, not tested. And this is not thread-safe.
package work.basil.example.time;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Objects;
public class LazyDate
{
// Member fields.
private long tolerance; // The number of nanoseconds by which we are willing for the cached date to be stale and possibly incorrect.
private final ZoneId zoneId;
private LocalDate cachedDate;
private long nanosWhenCached;
// Constructor.
public LazyDate ( final Duration theTolerance , final ZoneId theZoneId )
{
// Process arguments.
this.tolerance = theTolerance.toNanos ( );
this.zoneId = Objects.requireNonNull ( theZoneId );
// Initialize state.
this.nanosWhenCached = System.nanoTime ( );
this.cachedDate = LocalDate.now ( this.zoneId );
}
// Business logic.
public LocalDate today ( )
{
if ( ( System.nanoTime ( ) - this.nanosWhenCached ) > this.tolerance )
{
this.nanosWhenCached = System.nanoTime ( );
this.cachedDate = LocalDate.now ( this.zoneId );
System.out.println ( "TRACE - Updated cached date at " + Instant.now ( ) ); // For demo-only. Comment-out in production code.
}
return this.cachedDate;
}
}
Exercise that code:
LazyDate lazyDate = new LazyDate ( Duration.ofSeconds ( 3 ) , ZoneId.of ( "America/Edmonton" ) );
IntStream.range ( 1 , 7 ).forEach ( i -> { System.out.println ( lazyDate.today ( ) ); } );
try { Thread.sleep ( Duration.ofSeconds ( 4 ) ); } catch ( InterruptedException e ) { throw new RuntimeException ( e ); }
IntStream.range ( 1 , 7 ).forEach ( i -> { System.out.println ( lazyDate.today ( ) ); } );
When run:
2024-11-03
2024-11-03
2024-11-03
2024-11-03
2024-11-03
2024-11-03
TRACE - Updated cached date at 2024-11-04T04:09:06.990516Z
2024-11-03
2024-11-03
2024-11-03
2024-11-03
2024-11-03
2024-11-03
Upvotes: 1
Reputation: 44061
The best solution within the scope of standard java (no 3rd party-libraries) is using the class LocalDate
. So far this answer is identical to that of @Chronio, but I differ in use. The factory method now()
cannot be so quick because it exploits the system timezone - maybe even using a synchronized approach anywhere in the depth of TimeZone.getDefault()
.
But the class LocalDate
is very quick if you use its factory methods with arguments of year, month and day-of-month. Here you are just setting the state members without any timezone calculation and without any calculation of gregorian calendar math (okay only quick year-dependent length-of-month-check is involved). Just see the source code.
Upvotes: 3
Reputation: 757
To get the current date/time, I find that LocalDate.now()
is faster than Calendar.getInstance()
. The LocalDate class can be found in the java.time
package, which is new since Java 8 (Of course, if you want to maintain compatibility with old Java versions, that may not be an option).
Upvotes: 5
Reputation: 1009
use the Date, instead of Calendar class it will give you the current date
Upvotes: 0