Java local variable vs global field - Performance - java

I have a private method which takes date in as String type and returns XMLGregorianCalendar object with specific format. This method is being invoked from various other methods within the same class.
The format defined as SimpleDateFormat which is same for every invocation. Below are the 2 versions of the method.
Version 1:
private XMLGregorianCalendar getXmlGregorianCalendar(final String strDt) throws ParseException, DatatypeConfigurationException{
Date date = null;
// local variable dtFormat
DateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd-hh.mm.ss");
if (strDt != null) {
date = dtFormat.parse(strDt);
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(date);
XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
return xmlCal;
}else {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar());
}
}
Version 2:
// declare date format once, global variable
private DateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd-hh.mm.ss");
// getter setter for dtFormat
private XMLGregorianCalendar getXmlGregorianCalendar(final String strDt) throws ParseException, DatatypeConfigurationException{
Date date = null;
if (strDt != null) {
date = this.dtFormat.parse(strDt); // of course, getDtFormat() can be used
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(date);
XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
return xmlCal;
}else {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar());
}
}
Version 1 vs Version 2. Which has better performance?
Note: The enclosed class is annotated as #Component and involved in creating response for my web service.

SimpleDateFormat is not thread-safe, so that would be something to consider. Performance has little to do with this question, unless you really don't have any other performance hotspots (which would be surprising).
You can however use a ThreadLocal SimpleDateFormat, but remember that ThreadLocals have their own issues, and it probably doesn't really matter.

If that SimpleDateFormat is a constant, you can make it static final and it would improve your performance just a little bit. But you should be aware of the concept of static fields in classes. static fields are allocated once in memory for all instances of your #Component class. So if this cannot creating a bug in your code, it's better to define it as a constant using static final keywords.

Related

Custom Comparator not sorting time

I am working on a project where I am comparing the date and time in a custom Comparator. I actually concatenated the date with date and time. When I debugged the issue, I realized that time is not getting sorted. Here is the snippet of my code from my Comparator.
Date dateObject1= new Date();
Date dateObject2 = new Date();
try {
dateObject1 = sdf.parse(date1 + "T" + time1);
dateObject2 = sdf.parse(date2 + "T" + time2);
} catch (Exception e) { }
if (dateObject1.compareTo(dateObject2) > 0)
return 1;
else if (dateObject1.compareTo(dateObject2) < 0)
return -1;
else
return 0;
Test cases:
1. date1 - 2019-12-13 , date2 - 2019-12-13
time1 - 08:00:00, time2 - 12:00:00
When i debugged the issue I found it's returning 0 for the above test case. I am not sure why it's happening but I intent to return -1 such that it's sorted in ascending order.
Please advice.
Your problem is here
} catch (Exception e) { }
You initialize your 2 dates, both of which get initialized to the current time (System.currentTimeMillis()).
Date dateObject1= new Date();
Date dateObject2 = new Date();
Your parsing then fails, but you swallow the exception so you never noticed it.
Then you try to sort two dates which are either exactly same, or separated by a couple of milliseconds, but are certainly unrelated to the actual timestamps that you're trying to sort.
Check the exception, fix the parsing, and then it will work.
java.time and Comparator.comparing … thenComparing
I don’t know what your Java version is. The following snippet works on Java 8 and above. The most important ideas can be applied on Java 6 and 7 too.
List<MyObject> listToBeSorted = Arrays.asList(
new MyObject("2019-12-12", "11:53:50"),
new MyObject("2019-12-11", "13:07:05"),
new MyObject("2019-12-13", "05:02:16"),
new MyObject("2019-12-11", "09:54:57"),
new MyObject("2019-12-12", "05:53:52"),
new MyObject("2019-12-13", "06:56:08"),
new MyObject("2019-12-12", "02:31:55"),
new MyObject("2019-12-11", "09:28:16"),
new MyObject("2019-12-11", "20:58:55"));
Comparator<MyObject> cmpr = Comparator.comparing(MyObject::getDate)
.thenComparing(MyObject::getTime);
listToBeSorted.sort(cmpr);
listToBeSorted.forEach(System.out::println);
Output is:
MyObject [date=2019-12-11, time=09:28:16]
MyObject [date=2019-12-11, time=09:54:57]
MyObject [date=2019-12-11, time=13:07:05]
MyObject [date=2019-12-11, time=20:58:55]
MyObject [date=2019-12-12, time=02:31:55]
MyObject [date=2019-12-12, time=05:53:52]
MyObject [date=2019-12-12, time=11:53:50]
MyObject [date=2019-12-13, time=05:02:16]
MyObject [date=2019-12-13, time=06:56:08]
You will observe that the objects have been sorted by date and objects with the same date also by time. Here is the MyObject class that I used:
public class MyObject {
LocalDate date;
LocalTime time;
public MyObject(String dateString, String timeString) {
date = LocalDate.parse(dateString);
time = LocalTime.parse(timeString);
}
public LocalDate getDate() {
return date;
}
public LocalTime getTime() {
return time;
}
#Override
public String toString() {
return "MyObject [date=" + date + ", time=" + time + "]";
}
}
The two key messages are:
Don’t keep your dates and times as strings in your objects. Keep proper date and time objects. It may require parsing strings when you build your objects, but everything else gets noticeably easier.
Don’t use Date and SimpleDateFormat at all. Use classes from java.time, the modern Java date and time API. In this case LocalDate and LocalTime. The SimpleDateFormat and Date classes are poorly designed and long outdated, the former in particular notoriously troublesome. The modern API is so much nicer to work with.
The advantage of the Comparator methods comparing and thenComparing is not so much that code gets considerably shorter. The really important gain is that writing comparators in this style is much less error prone, and the code reads more naturally.
What went wrong in your code?
The problem is in the line that you posted in a comment:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
This formatter only parses the date from each string and ignores the time. It’s one of many confusing traits of SimpleDateFormat that it is happy to parse only a part of the string and doesn’t draw our attention to the fact that some of the text is ignored — in this case the T and the entire time.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Just return the value of the comparison since that is what you return anyway.
Date dateObject1= new Date();
Date dateObject2 = new Date();
try {
dateObject1 = sdf.parse(date1 + "T" + time1);
dateObject2 = sdf.parse(date2 + "T" + time2);
} catch (Exception e) {
e.printStackTrace(); // always print these. They are there to help you.
}
return dateObject1.compareTo(dateObject2);

convert xmlGregorianCalendar to Date and reverse

i all, i have a spring boot application. what i want in specific is to convert a class (that have nestet object field) in his corrispective entity. example:
public class example{
String string;
ObjectExample object;
}
public class ObjectExample{
String oneString;
XMLGregorianCalendar date;
}
this 2 object are also marked in another package as entities, but ovviusly in the ObjectExampleEntity i have Date date instead XMLGregorianCalendar, like this with the example
#Entity
public class example{
String string;
ObjectExample object;
}
#Entity
public class ObjectExample{
String oneString;
Date date;
}
because i have a big model and big entity (this above is only an example) with a lot of nested classes , i use dozer to convert from the model to the class.
consider for example that the repository jpa is only created for the father example class.
i want to know how i can with dozer convert from Date (entity) to XMLGregorianCalendar (model) and reverse. the model and the entity,i repeat are equal. the only difference is the type of the date. thanks
I am assuming:
Since your variable is named date it contains a calendar date (without time of day).
You are tied to XMLGregorianCalendar because of a WSDL outside your control, but you can change type on the entity side.
Based on these assumptions I recommend LocalDate on the entity side. It’s part of java.time, the modern Java date and time API, and represents exactly a date without time of day. The Date class that you used is poorly designed, long outdated and not recommended. Also despite the name a Date never represented a date, but a point in time.
There are more options. I am presenting three.
Option 1: transfer individual fields
From XMLGregorianCalendar to LocalDate:
DatatypeFactory xmlFactory = DatatypeFactory.newInstance();
XMLGregorianCalendar wsDate = xmlFactory
.newXMLGregorianCalendarDate(2019, DatatypeConstants.MARCH, 30,
DatatypeConstants.FIELD_UNDEFINED);
// Validate
if ((wsDate.getHour() != 0 && wsDate.getHour() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getMinute() != 0 && wsDate.getMinute() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getSecond() != 0 && wsDate.getSecond() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getMillisecond() != 0 && wsDate.getMillisecond() != DatatypeConstants.FIELD_UNDEFINED)) {
System.out.println("Warning: time of day will be lost in conversion");
}
if (wsDate.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
System.out.println("Warning: UTC offset will be lost in conversion");
}
// Convert
LocalDate entityDate = LocalDate.of(wsDate.getYear(), wsDate.getMonth(), wsDate.getDay());
System.out.println(entityDate);
The output is in this case:
2019-03-30
From LocalDate to XMLGregorianCalendar:
LocalDate entityDate = LocalDate.of(2019, Month.MARCH, 31);
XMLGregorianCalendar wsDate = xmlFactory.newXMLGregorianCalendarDate(
entityDate.getYear(),
entityDate.getMonthValue(),
entityDate.getDayOfMonth(),
DatatypeConstants.FIELD_UNDEFINED);
System.out.println(wsDate);
2019-03-31
Advantage of this way: It’s pretty straightforward. Disadvantage: You and your reader need to take care that the fields are mentioned in the right order.
Option 2: convert via strings
// Validate as before
// Convert
LocalDate entityDate = LocalDate.parse(wsDate.toXMLFormat());
Result is as before.
XMLGregorianCalendar wsDate
= xmlFactory.newXMLGregorianCalendar(entityDate.toString());
Advantage: it’s brief, and there’s no surprise that the results are correct. Disadvantage: To me it feels like a waste to format into a string only to parse it back.
Option 3: convert via GregorianCalendar and ZonedDateTime
ZonedDateTime zdt = wsDate.toGregorianCalendar().toZonedDateTime();
// Validate
if (! zdt.toLocalTime().equals(LocalTime.MIN)) {
System.out.println("Warning: time of day will be lost in conversion");
}
if (! zdt.getZone().equals(ZoneId.systemDefault())) {
System.out.println("Warning: UTC offset will be lost in conversion");
}
// Finish conversion
LocalDate entityDate = zdt.toLocalDate();
And the other way:
// It doesn’t matter which time zone we pick
// since we are discarding it after conversion anyway
ZonedDateTime zdt = entityDate.atStartOfDay(ZoneOffset.UTC);
GregorianCalendar gCal = GregorianCalendar.from(zdt);
XMLGregorianCalendar wsDate = xmlFactory.newXMLGregorianCalendar(gCal);
wsDate.setTime(DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED,
DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED);
wsDate.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
The validation I present here is a bit simpler but also not quite so strict. If you want strict validation, you can just use the validation from before.
Advantages: I think it’s the official way; at least it uses the conversion methods offered. What I like is that the conversion itself is direct and brief. Disadvantage: When converting to XMLGregorianCalendar we need to set the unused fields to undefined manually, which makes it wordy.
Conclusion
I have presented three options each with their pros and cons. You may also mix, of course, but using a similar conversion both ways is probably less confusing in the end.

Formatting date in Apache JsonToStringStyle

I am implementing toString methods for my entities. There are a lot of fields of type ZonedDateTime. Unfortunately, it's converted to extremely long text representation:
java.util.GregorianCalendar[
time=1545826815293,
areFieldsSet=true,
areAllFieldsSet=true,
lenient=true,
zone=sun.util.calendar.ZoneInfo
[
id=
"UTC",
offset=0,
dstSavings=0,
useDaylight=false,
transitions=0,
lastRule=null
],
firstDayOfWeek=1,
minimalDaysInFirstWeek=1,
ERA=1,
YEAR=2018,
MONTH=11,
WEEK_OF_YEAR=52,
WEEK_OF_MONTH=5,
DAY_OF_MONTH=26,
DAY_OF_YEAR=360,
DAY_OF_WEEK=4,
DAY_OF_WEEK_IN_MONTH=4,
AM_PM=1,
HOUR=0,
HOUR_OF_DAY=12,
MINUTE=20,
SECOND=15,
MILLISECOND=293,
ZONE_OFFSET=0,
DST_OFFSET=0
]
How can I format it using SimpleDateFormat?
I tried the example given here:
https://howtodoinjava.com/apache-commons/how-to-override-tostring-effectively-with-tostringbuilder/
public class CustomToStringStyle extends ToStringStyle
{
private static final long serialVersionUID = 1L;
protected void appendDetail(StringBuffer buffer, String fieldName, Object value)
{
if (value instanceof Date)
{
value = new SimpleDateFormat("yyyy-MM-dd").format(value);
}
buffer.append(value);
}
}
But in this case, I don't use JSON style of formatting. I can't extend JsonToStringStyle because it's private.
ZonedDateTime is part of the new Java 8 API while SimpleDateFormat is the old buggy Date formatter. You need to use the new DateFormatter for Java 8 date/time classes.
The JSON you posted however is neither a ZonedDateTime, nor a Date, it is a GregorianCalendar, so not sure whether your problem is really to do with ZonedDateTime as you are saying.
GregorianCalendar is not Date, you can convert it to Date, then use the code in your link:
if (value instanceof calendar)
{
Date date = new Date(value.getTimeInMillis());
value = new SimpleDateFormat("yyyy-MM-dd").format(date);
}
I would suggest to use java.time package to deal with date and time.

Java system time

I have this code copied from one of questions from SO:
public static String getCurrentTimeStamp() {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
String strDate = sdfDate.format(now);
return strDate;
}
I want to get only the system time and NOT the date. Then I must change second line of code to:
SimpleDateFormat sdfDate = new SimpleDateFormat(" HH:mm:ss") ;
Then, DATE() must get the current time. Clear upto this point but I can't understand the format() function used.
I mean cant we simply output variable now instead of strdate?
Is it just because that the return type of function getCurrentTimeStamp() is String?
Please clarify and if there is any other simpler and one line code for getting system time alone, do share.
I mean cant we simply output variable now instead of strdate.
Well you could return now.toString() - but that will use the format that Date.toString() happens to choose, whereas you want a specific format. The point of the SimpleDateFormat object in this case is to convert a Date (which is a point in time, without reference to any particular calendar or time zone) into a String, applying an appropriate time zone, calendar system, and text format (in your case HH:mm:ss).
You can still simplify your method somewhat though, by removing the local variables (which are each only used once):
public static String getCurrentTimeStamp() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
Or maybe you'd find it more readable to keep the variable for the date format, but not the date and the return value:
public static String getCurrentTimeStamp() {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
return format.format(new Date());
}
Personally I'd recommend using Joda Time instead, mind you - it's a much nicer date/time API, and its formatted are thread-safe so you could easily keep a reference to a single formatting object.
public static String getCurrentTimeStampwithTimeOnly() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
Helps you to do this.
you can call this line any time
Date now = new Date();
The now variable will contain the current timestamp
The format function just generates a String from this timestamp
also take a look at the Calendar class ( Calendar.getInstance())

Synchronizing access to SimpleDateFormat

The javadoc for SimpleDateFormat states that SimpleDateFormat is not synchronized.
"Date formats are not synchronized. It
is recommended to create separate
format instances for each thread. If
multiple threads access a format
concurrently, it must be synchronized
externally."
But what is the best approach to using an instance of SimpleDateFormat in a multi threaded environment. Here are a few options I have thought of, I have used options 1 and 2 in the past but I am curious to know if there are any better alternatives or which of these options would offer the best performance and concurrency.
Option 1: Create local instances when required
public String formatDate(Date d) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(d);
}
Option 2: Create an instance of SimpleDateFormat as a class variable but synchronize access to it.
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
synchronized(sdf) {
return sdf.format(d);
}
}
Option 3: Create a ThreadLocal to store a different instance of SimpleDateFormat for each thread.
private ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();
public String formatDate(Date d) {
SimpleDateFormat sdf = tl.get();
if(sdf == null) {
sdf = new SimpleDateFormat("yyyy-MM-hh");
tl.set(sdf);
}
return sdf.format(d);
}
Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.
OK if you can live with a bit of blocking. Use if formatDate() is not used much.
Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.
For applications both 2. and 3. are viable options. Which is best for your case depends on your use case. Beware of premature optimization. Only do it if you believe this is an issue.
For libraries that would be used by 3rd party I'd use option 3.
The other option is Commons Lang FastDateFormat but you can only use it for date formatting and not parsing.
Unlike Joda, it can function as a drop-in replacement for formatting.
(Update: Since v3.3.2, FastDateFormat can produce a FastDateParser, which is a drop-in thread-safe replacement for SimpleDateFormat)
If you are using Java 8, you may want to use java.time.format.DateTimeFormatter:
This class is immutable and thread-safe.
e.g.:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = new java.util.Date().toInstant()
.atZone(ZoneId.systemDefault())
.format(formatter);
Commons Lang 3.x now has FastDateParser as well as FastDateFormat. It is thread safe and faster than SimpleDateFormat. It also uses the same format/parse pattern specifications as SimpleDateFormat.
Don't use SimpleDateFormat, use joda-time's DateTimeFormatter instead. It is a bit stricter in the parsing side and so isn't quite a drop in replacement for SimpleDateFormat, but joda-time is much more concurrent friendly in terms of safety and performance.
I would say, create a simple wrapper-class for SimpleDateFormat that synchronizes access to parse() and format() and can be used as a drop-in replacement. More foolproof than your option #2, less cumbersome than your option #3.
Seems like making SimpleDateFormat unsynchronized was a poor design decision on the part of the Java API designers; I doubt anyone expects format() and parse() to need to be synchronized.
Another option is to keep instances in a thread-safe queue:
import java.util.concurrent.ArrayBlockingQueue;
private static final int DATE_FORMAT_QUEUE_LEN = 4;
private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
private ArrayBlockingQueue<SimpleDateFormat> dateFormatQueue = new ArrayBlockingQueue<SimpleDateFormat>(DATE_FORMAT_QUEUE_LEN);
// thread-safe date time formatting
public String format(Date date) {
SimpleDateFormat fmt = dateFormatQueue.poll();
if (fmt == null) {
fmt = new SimpleDateFormat(DATE_PATTERN);
}
String text = fmt.format(date);
dateFormatQueue.offer(fmt);
return text;
}
public Date parse(String text) throws ParseException {
SimpleDateFormat fmt = dateFormatQueue.poll();
if (fmt == null) {
fmt = new SimpleDateFormat(DATE_PATTERN);
}
Date date = null;
try {
date = fmt.parse(text);
} finally {
dateFormatQueue.offer(fmt);
}
return date;
}
The size of dateFormatQueue should be something close to the estimated number of threads which can routinely call this function at the same time.
In the worst case where more threads than this number do actually use all the instances concurrently, some SimpleDateFormat instances will be created which cannot be returned to dateFormatQueue because it is full. This will not generate an error, it will just incur the penalty of creating some SimpleDateFormat which are used only once.
I just implemented this with Option 3, but made a few code changes:
ThreadLocal should usually be static
Seems cleaner to override initialValue() rather than test if (get() == null)
You may want to set locale and time zone unless you really want the default settings (defaults are very error prone with Java)
private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() {
#Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
return sdf;
}
};
public String formatDate(Date d) {
return tl.get().format(d);
}
Imagine your application has one thread. Why would you synchronize access to SimpleDataFormat variable then?

Categories