1

I wanted to create my own basic console log for a small Java utility program I'm developing. Everything is working great in basically real-time; however, there is a small piece of code that is bothering me. I shall give a brief rundown of all the code below.

NOTE: Almost all the code here is untested/unoptimized. If there is anything wrong that I am doing, please let me know, I'm very open to criticism.

Static variables used for logging purposes:

static ArrayList<String> logs = new ArrayList<>();
static int offset = 0;
static DateFormat dateFormat = new SimpleDateFormat("yyy/MM/dd HH:mm:ss");
static Date date = new Date();

Code that populates the JTextArea - This appends the log messages and keeps in mind the offset (so I don't iterate through the whole ArrayList for something that has already been logged).

public static void populateTextArea(final JTextArea textArea) {
  for(int i = offset; i < logs.size()-1; i++) {
      textArea.append(dateFormat.format(date) + ":  " + logs.get(i)+"\n");
  }
      offset = logs.size()-1;
}

The following automatically updates the console every 150ms:

public static void timerLogging(final JTextArea textArea) {
Timer timer = new Timer(150, new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent e)
       {
           updateDateAndTime();
           populateTextArea(textArea);
       }
    });
    timer.start();
}

Now the following part is what bothers me:

public static void updateDateAndTime() {
    date = new Date();
}

Is there a way to simply do something such as date = GlobalDate.getCurrentDate();? With the current way it's set up, won't it create a small overhead (probably not even noticeable however I'm not a 'just get it to work' kind of guy)?

Sildoreth
  • 1,883
  • 1
  • 25
  • 38
Juxhin
  • 5,068
  • 8
  • 29
  • 55
  • 2
    Remember that if you spend time worrying about small overheads, you'll have that much less time to spend on things that actually matter. – Kayaman Apr 21 '15 at 13:20
  • Mind that if you change the value of that `Date` instance, then you'll change it for "everyone" that refers to the same instance. – Tom Apr 21 '15 at 13:22
  • Good point Kayaman. @Tom - It's only used for the console, it would indeed be safe in this case I would presume. – Juxhin Apr 21 '15 at 13:23
  • Just as a sidenote - you seem to be using global SimpleDateFormat instance and TimerTask. If you happen to use multiple TimerTasks in your code have in mind that SimpleDateFormat is not thread safe (not a problem with code as you have it now but might be if you use multiple TimerTasks) – Krešimir Nesek Apr 21 '15 at 13:25
  • @KresimirNesek Could you elaborate? I have a simple understanding of what you're trying to say but I'm not grasping to the full. – Juxhin Apr 21 '15 at 13:26
  • Premature optimization is the root of all evil. – Andreas Apr 21 '15 at 13:26
  • @Andreas - Well I don't really want to call it optimization, just my way of doing it seemed very ugly to my eyes and wanted to know if there was a simpler alternative. I knew it wouldn't optimize much if anything at all. – Juxhin Apr 21 '15 at 13:27
  • @Juxhin Are you optimizing the aesthetics of code that is bug free and straight-forwardly readable? If not optimization, call it golden polish; avoid none the less. – Andreas Apr 21 '15 at 13:31
  • @Juxhin Example where SimpleDateFormat might cause problems due to threading is if you format a date in your TimerTask (which executes in a separate thread) and you also use the same instance some where else (for example in your code or from a separate TimerTask) at the same time. – Krešimir Nesek Apr 21 '15 at 13:32
  • Thanks to both Andreas and Kresimir. Both points have been noted and securely absorbed :) – Juxhin Apr 21 '15 at 13:33
  • Instantiating the object is so ridiculously cheap, why bother? – chrylis -cautiouslyoptimistic- Apr 21 '15 at 13:42
  • By the way, you might consider using [`ScheduledExecutorService`](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) rather than `Timer`. – Basil Bourque Apr 22 '15 at 08:55
  • @BasilBourque - May I ask why ought to opt for `ScheduledExecutorService` instead? I have set it up with `Timer` so far and wouldn't mind changing it if there is a good reason to do so. – Juxhin Apr 22 '15 at 09:04
  • For comparing `Timer` vs `ScheduledExecutorService`, read [this](http://stackoverflow.com/q/409932/642706) and [this](http://stackoverflow.com/q/2213109/642706) on StackOverflow. And be sure to read the book, *Java Concurrency in Practice*, by Brian Goetz. – Basil Bourque Apr 22 '15 at 18:11
  • Thanks a million @BasilBourque - will definitely do that. – Juxhin Apr 23 '15 at 07:09

3 Answers3

4

There is no "overhead" to speak about: creating one additional object seven times a second does not count as an overhead.

However, if you would like to keep the same Date object, you can re-initialize it in the same way the default constructor of Date uses:

public static void updateDateAndTime() {
    date.setTime(System.currentTimeMillis());
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • A kitten dies everytime you mutate a date. I much prefer your first implementation where you create a new date each time. Each log is done at a different time, it should be a different Date object. – Guillaume Apr 21 '15 at 13:37
  • @Guillaume - I'm almost tempted to ask a question on that specifically, there seem to be a lot of comments regarding `java.lang.Date` being a bit of an issue. However I'm sure there are already a bunch of questions on that already. – Juxhin Apr 21 '15 at 13:39
  • @Juxhin I wish `java.util.Date` were immutable, but since the designers have deprecated all mutating methods except `setTime` it looks to me that they intentionally kept the class mutable. I do not subscribe to the dying kittens doctrine, though: I think it is OK to use all features of an API even if you disagree with its designers on philosophical grounds :) – Sergey Kalinichenko Apr 21 '15 at 13:50
  • Well, for my intended use, it seems to work fine so I'll keep it this way. I do understand their point and to a certain degree agree with it. Thanks again everyone for your input. – Juxhin Apr 21 '15 at 13:52
2

The first place to check any time you see Java and Date in the same sentence is Joda Time.

java.lang.Date is mutable, so you could keep the same date object and change the date it represent:

date.setTime(System.currentTimeMillis());

The fact that java.lang.Date is mutable is usually considered a mistake in the way the Java API was designed. Today and Tomorrow are not the same date, they should not be the same object.

Both Joda-Time and the new java.time package in Java 8 largely use immutable objects.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Guillaume
  • 18,494
  • 8
  • 53
  • 74
  • I have indeed heard and read about Joda Time before (actually used it like a year ago but forgot for what). I've read a comment from a certain Jon (well known on SO) who said the same thing, may I ask why this is considered a flaw within the Java API? Because you can actually change the current Date? – Juxhin Apr 21 '15 at 13:36
  • @Juxhin No, the problem is not changing the current time of the clock in the JVM or host OS, as java.util.Date cannot do so. Read [Wikipedia page](https://en.m.wikipedia.org/wiki/Immutable_object) and google to learn more about immutable objects. – Basil Bourque Apr 21 '15 at 17:10
  • 1
    The problem with mutable `Date`s is that you tend to pass `Date` objects around. If they are mutable, it means that they can potentially change while you are using them, which is most of the time unexpected. – Guillaume Apr 21 '15 at 18:42
1

Avoid Premature Optimization

As noted by the other two Answers, generally you should not worry about the overhead of creating objects in Java. At least the implementation provided by Oracle is highly optimized for creation and destruction of short-lived objects. Several per second is not a problem.

While coding a long-running tight loop, you might briefly consider re-using objects. But only devote yourself to reducing instantiation when you've proven a performance problem. Otherwise you fall into the trap of premature optimization.

ISO 8601

The ISO 8601 standard defines sensible formats for textual representations fo date-time values. The YYYY-MM-DDTHH:MM:SS.SSSZ format makes sense for logging.

UTC

Almost always better to work with date-time values in the UTC time zone, then adjust to a local time zone for presentation to the user. This means your business logic, your database storage, and your logging should be done in UTC.

Joda-Time & java.time

The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome, confusing, and flawed. Avoid them.

Instead use either the Joda-Time library or the new java.time package built into Java 8 (inspired by Joda-Time).

Both Joda-Time and java.time use ISO 8601 as their defaults when generating or parsing strings. The java.time classes extend ISO 8601 by appending the name of the time zone, in addition to the usual offset number.

Example Code – Joda-Time

Example code using Joda-Time 2.7.

DateTime nowUtc = DateTime.now( DateTimeZone.UTC ) ;

Example output.

2013-08-21T00:16:26.941+09:00

For presentation to user, assign another time zone.

DateTime nowQuébec = nowUtc.withZone( DateTimeZone.forID( "America/Montreal" ) ) ;

Example Code – java.time

Example code using java.time in Java 8.

ZonedDateTime nowUtc = ZonedDateTime.now( ZoneOffset.UTC );

Example output.

2013-08-21T00:16:26.941+09:00[Asia/Tokyo]

Adjusting the time zone.

ZoneId zone = ZoneId.of("America/Montreal"); 
ZonedDateTime nowQuébec = ZonedDateTime.of( nowUtc , zone ) ;

Joda-Time Performance

Creating several DateTime instances per second is not a problem.

To quote from the FAQ entry, How well does it perform?

Joda-Time is designed for performance. Compared to java.util.Calendar, java.text.SimpleDateFormat, and java.util.TimeZone, nearly all equivalent operations in Joda-Time are faster. The significant exceptions are operations to get or set an individual field.

Calling "get" on java.util.Calendar is very fast because it doesn't do any work. Calendar calculates all fields in advance, even if many of those fields you won't need. Calendar's set method is fast because it defers calculations until later. Calling Calendar.get after calling Calendar.set forces all the field values to be re-calculated. Calling Joda's DateTime.get method after calling DateTime.set only performs the minimum amount of calculations, and the pair is faster than Calendar.

Joda-Time also allocates very few temporary objects during operations, and performs almost no thread synchronization. In systems that are heavily multi-threaded or use a lot of memory, Calendar, SimpleDateFormat, and TimeZone can become bottlenecks. When the Joda-Time classes are used instead, the bottlenecks go away.

Executor

You may want to replace your use of Timer with an Executor, specifically a ScheduledExecutorService. Timer is largely outmoded by Executor. Especially in Servlet/Java EE web apps, you should never use Timer. Search StackOverflow and Google for more information.

Logging Framework

If your Question is truly regarding logging, I suggest learning to use one of the several good logging frameworks available in Java.

slf4j

First step is to get slf4j. This project is only interfaces, an API abstraction.

Because there are so many logging frameworks in Java, you may encounter different ones in different projects, or you may want to switch frameworks in your own projects. The catch is that logging means you have many calls sprinkled throughout your codebase. So learning or changing frameworks is troublesome. The slf4j project aims to solve this problem by providing a set of interfaces you call throughout your code. Behind the scenes as adapter intervenes to translate those slf4j calls into calls for your specific logging framework. Adapters exist for log4j, Java’s java.util.logging, and more.

Actually, slf4j also comes bundled with a very simple implementation of the interfaces. This implementation is meant only for development (to get you started), or for use in very simple projects. By default that implementation logs only a few of the log levels, but you can adjust that. You can start with this simple imlementation and later switch to Logback; that is the entire point to slf4j, enabling you to switch logging frameworks.

Logback

Furthermore, the creator of slf4j also created Logback. Logback is a full-featured logging framework that directly implements the interfaces of slf4j. Being a direct implementation of the interfaces means no adapter is needed.

By the way, the creator of slf4j and Logback, Ceki Gülcü, is also the creator of the famous log4j. He took his vast experience in logging to create a successor, slf4j & Logback.

One striking flaw in Logback is its lack of support for ISO 8601 formatting of the date-times (as mentioned above). You can adjust that, per this StackOverflow Question.

For new projects I recommend starting with slf4j + Logback. For existing projects using another logging framework, keep that logging framework in place while making calls through slf4j via an adapter. Someday in the future, after all the logging calls have been replaced with slf4j calls, you have the option of replacing your logging framework with Logback if your needs warrant.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thank you very much for your inpit Basil you presented some really good points. I stripped out the whole logging aspect of the application for now and was thinking of re-implementing it ypur way or simply using log4j. – Juxhin May 02 '15 at 09:44
  • No, not `log4j`, that’s so last millennium. Check out [slf4j](http://en.m.wikipedia.org/wiki/SLF4J) (an API abstraction, just interfaces) and [logback](http://logback.qos.ch) (an implementation). – Basil Bourque May 02 '15 at 15:39
  • Haha well yes, 1993 (or something like that) is indeed a bit old. I did check out `SLF4J` so I'll implement that with `logback` or once again, just write my own very basic one (since I don't need anything elaborate). However I do wish to learn to properly implement SLF4J into my future applications. – Juxhin May 02 '15 at 15:43
  • Actually slf4j includes a very simple implementation that may suit your needs. By default it only logs some priority levels but you can adjust that. Also, fun fact, slf4j and logback are built by the inventor of log4j. – Basil Bourque May 02 '15 at 15:54
  • Which begs me to ask, why is `log4j` still being used then? - Regarding SLF4J, I'll be honest and say I'm very weak when it comes to implementing foreign APIs. I've been ranted on about how important they are time and time again but I always found myself using pure Java API instead (which I'm not saying with pride). So I simply need to force myself out of my comfort zone to get to using SLF4J. Thanks for the numerous tips and great post. I simply had to mark it as the proper answer. – Juxhin May 02 '15 at 16:01