Item 6: Avoid Creating Unnecessary Objects - saurabhojha/Effective-java GitHub Wiki

There are two use cases, where one should avoid creating new objects.

  1. The object in use is immutable.
  2. The object in use is mutable, but does not change.

1. The object in use is immutable:

Consider the code given below:

package item6;

public class creatingObjects {

    public static String getNewString() {
        return new String("String");
    }

    public static String getSingleString() {
        return "String";
    }
    public static  void  main(String args[]) {

        String s;
        System.out.println("Creating String using new Operator:");
        for(int i=1;i<=5;i++) {
            s = getNewString();
            System.out.println(System.identityHashCode(s));
        }
        
        System.out.println();
        System.out.println("Creating String without using new Operator:");
        for(int i=1;i<=5;i++) {
            s = getSingleString();
            System.out.println(System.identityHashCode(s));
        }
    }
}

If you run the code given above, it gives the following output:


Creating String using new Operator:
258952499
603742814
1067040082
1325547227
980546781

Creating String without using new Operator:
2061475679
2061475679
2061475679
2061475679
2061475679

Process finished with exit code 0

Every time a string is created using the new operator, we get a different identity hash code. Since strings are immutable, it makes more sense to use the second function getSingleString() instead of getNewString(), to avoid unnecessary creation of objects.

Using static factory method to limit object creation:

Static factory method are a great way to reduce the number of objects being created. Since static factories are not bound to create a new object every time they are invoked, they provide a great mechanism for caching objects that are going to be used frequently. In case the object is eagerly initialised, there can be situation where the object is not used at all. However implementing lazy loading is complex and can be avoided unless absolutely necessary.

Unintentional Object creation with autoboxing:

Consider the following piece of code. We have two functions, findSumWrapper and findSumPrimitive. The only difference in the two functions is that of the type of variable sum. findSumWrapper uses the object Long, and findSumPrimitive uses the primitive long, to store the sum per iteration.

package item6;

public class creatingObjects {

    public static void findSumWrapper() {
        Long sum = 0L;
        for(long i=0;i<=Integer.MAX_VALUE;i++)
        {
            sum+=i;
        }
    }

    public static void findSumPrimitive() {
        long sum = 0L;
        for(long i=0;i<=Integer.MAX_VALUE;i++)
        {
            sum+=i;
        }
    }

    public static  void  main(String args[]) {

        long start = System.currentTimeMillis();
        findSumWrapper();
        long end   = System.currentTimeMillis();
        long totalTime = (end-start);
        System.out.println("time taken in ms for findSumWrapper: "+totalTime);

        start = System.currentTimeMillis();
        findSumPrimitive();
        end   = System.currentTimeMillis();
        totalTime = (end-start);
        System.out.println("time taken in ms for findSumPrimitive: "+totalTime);
    }
}

The code gives the following output on execution:

time taken in ms for findSumWrapper: 5215
time taken in ms for findSumPrimitive: 674

Process finished with exit code 0

The difference in performance emerges from the fact that in the findSumWrapper function, the primitive variable i is boxed into type Long every iteration. This is repeated for 2^31 iterations, causing significant overhead to a trivial code. Hence, one must look out for such typos and prevent accidental overheads.

Another way to reduce object creation is by maintaining our own object pool. This however, should be done in situation where the cost of creation of objects outweigh the nuances of maintaining the object pool (for example, maintaining object pool for database connection resources).

A great read on the topic of smart object management can be found here and here.