Wednesday, October 21, 2015

Effective Java - Item 5 : Avoid creating unnecessary objects

Whenever I had to create a string I always had the confusion of doing it either this way
String message = new String("hello world");
or this way
String message = "hello world";
Programatically both are correct but one of them is better than the other. Let us look at which one.

The first method creates a new String object in the heap whenever it is executed. But what we are passing as an argument to to the constructor of String class to create the object is itself an instance of String class.
The second method will create an instance of String in a section of the heap known as String constant pool.
Now if both of the above lines are executed for a second time the first line will create a new String object on the heap but the second line will reuse the object it created the first time from the String constant pool. It might help you to understand it visually to remember it for a longer time.

So the second method is a better option since it does not create new instances and crowd the heap needlessly. 
This was the case with immutable objects like String. We can also avoid creating unnecessary objects and reuse in case of some mutable objects too. Let us look at an example.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Commodity {

 private final Date manufactureDate;
 private final int validity;
 
 public boolean isExpired(){
  Calendar gmtCal =
    Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  Date today = gmtCal.getTime();
  gmtCal.setTime(this.manufactureDate);
  gmtCal.add(Calendar.MONTH, this.validity);
  Date expiryDate = gmtCal.getTime();
  
  return expiryDate.compareTo(today) > 0;
 }
}

Here the method isExpired creates the Calendar and Date objects each time it is invoked. These are very expensive objects to create. So how can we avoid this? 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Commodity {

 private final Date manufactureDate;
 private final int validity;
 private static final Calendar gmtCal;
 private static final Date today;
 
 static {
  gmtCal =
    Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  today = gmtCal.getTime();
 }
 
 public boolean isExpired(){
  gmtCal.setTime(this.manufactureDate);
  gmtCal.add(Calendar.MONTH, this.validity);
  Date expiryDate = gmtCal.getTime();
  
  return expiryDate.compareTo(today) > 0; 
 }
}
So here as an improvisation we can include the expensive object creation code in a static block so that it will be invoked only once when it is initialized and not each time the method is invoked.
Apart from achieving a significant gain in performance it also makes the code better organized and understandable.

Another instance where we usually create unnecessary objects is places where we use boxed primitives instead of primitive types
1
2
3
4
5
6
7
public static void main(String[] args) {
   Long sum = 0L;
   for (long i = 0; i < Integer.MAX_VALUE; i++)  {
     sum += i;
   }
   System.out.println(sum);
}
The above code segment calculates the sum of all positive int values. The variable sum is declared as a Long but inside the for loop it is being assigned a primitive value long. This is supported in Java by means of autoboxing. So this creates a lot of  unnecessary Long objects which increases the runtime. Thus it is always better to prefer primitive types.

After looking at the above instances you might wonder if object creation in general is a very bad thing to do. Well, it is not the case. The author makes sure to clarify that only creating unnecessary objects (especially when dealing with heavyweight objects e.g. database connection objects) should be avoided. 

No comments:

Post a Comment