2015-03-08

Not everything in java is an object !

Java has a dual-type system, include Primitive Types and Object Types. Primitive types are value-based, but object types are reference-based.

There are eight primitive types predefined in Java. For each primitive type there is a corresponding wrapper class that is an object type.

Why use primitive type ?

The most important factor to consider for using primitive types in Java are performance, software performance is usually measured in terms of space and time.

space. java can use primitive types to create automatic variables (local variable 局部变量) that are not references. The variables hold the value, and it’s place on the stack(栈) so its much more efficient.

Creating an object using new isn’t very efficient because new will place objects on the heap(堆). This approach would be very costly for small and simple variables.

a single double occupy 8 bytes , a single Double occupy 24 bytes.
primitive types创建的局部变量所占的空间,方法执行完后自动被释放掉;object types所占的空间,需触发GC回收, 方才释放。

Runtime Performance
To compare the runtime performances for primitives and objects, we need an algorithm dominated by numerical calculations. For this article I have chosen matrix multiplication, and I compute the time required to multiply two 1000-by-1000 matrices.
MacBook Pro
处理器:2.7 GHz Intel Core i5
内存:8 GB 1867 MHz DDR3
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
public static void main(String[] args) {
        // all matrices are n by n
        int n = 1000;

        // initialize matrices
        double a1[][] = new double[n][n];
        double b1[][] = new double[n][n];
        Double a2[][] = new Double[n][n];
        Double b2[][] = new Double[n][n];

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                double xa = Math.random();
                a1[i][j] = xa;
                a2[i][j] = xa;

                double xb = Math.random();
                b1[i][j] = xb;
                b2[i][j] = xb;
            }
        }

        StopWatch timer = new StopWatch();

        // test first with matrices of double
        timer.start();
        double c1[][] = MatrixUtil.multiply(a1, b1);
        timer.stop();

        double totalTime1 = timer.getElapsedTimeInSeconds();
        System.out.println("total time in for double = " + totalTime1 + " seconds");
        System.out.println();

        // test second with matrices of Double
        timer.start();
        Double c2[][] = MatrixUtil.multiply(a2, b2);
        timer.stop();

        double totalTime2 = timer.getElapsedTimeInSeconds();
        System.out.println("total time for Double = " + totalTime2 + " seconds");
        System.out.println();
    }
total time in for double = 5.379 seconds
total time for Double = 29.36 seconds
    
public static void main(String[] args)
      {
        int n = 1000;

        long bytesUsingWrappers   = getBytesUsingWrappers(n);
        long bytesUsingPrimitives = getBytesUsingPrimitives(n);

        System.out.println("Using wrappers:");
        System.out.println("- total bytes = " + bytesUsingWrappers);
        System.out.println("- bytes per element = " + ((double) bytesUsingWrappers)/(n*n));
        System.out.println();
        System.out.println("Using primitives:");
        System.out.println("- total bytes = " + bytesUsingPrimitives);
        System.out.println("- bytes per element = " + ((double) bytesUsingPrimitives)/(n*n));
      }

    /**
     * Returns the number of bytes used by an n-by-n matrix of type double.
     * @param  the number of rows and columns of the matrix 
     */
    public static long getBytesUsingPrimitives(int n)
      {
        System.gc();   // force garbage collection
        long memStart = Runtime.getRuntime().freeMemory();
        double[][] a = new double[n][n];

        // put some random values in the matrix
        for (int i = 0;  i < n;  ++i)
          {
            for (int j = 0; j < n;  ++j)
                a[i][j] = Math.random();
          }

        long memEnd = Runtime.getRuntime().freeMemory();

        return memStart - memEnd;
      }

    /**
     * Returns the number of bytes used by an n-by-n matrix of type Double.
     * @param  the number of rows and columns of the matrix 
     */
    public static long getBytesUsingWrappers(int n)
      {
        System.gc();   // force garbage collection
        long memStart = Runtime.getRuntime().freeMemory();
        Double[][] a = new Double[n][n];

        // put some random values in the matrix
        for (int i = 0;  i < n;  ++i)
          {
            for (int j = 0; j < n;  ++j)
                a[i][j] = Math.random();
          }

        long memEnd = Runtime.getRuntime().freeMemory();

        return memStart - memEnd;
      }
  }
Using wrappers:
- total bytes = 27965112
- bytes per element = 27.965112

Using primitives:
- total bytes = 8756688
- bytes per element = 8.756688

Object types存在NullPointerException隐患

Usually I go with the primitives. However, one peculiarity of using classes like Integer and Boolean is the possibility of assigning null to those variables. Of course, this means that you have to do null checks all the time, but still better to get a NullPointerException than to have logic errors due to using some int or boolean variable that has not been initialized correctly.

Be careful with autoboxing and unboxing.

Because autoboxing and unboxing take time, and because intermediate wrapper objects are created during the boxing operation (which can lead to garbage collection), never box/unbox in a lengthy loop, especially where performance is critical.

Summary

Primitives are essential for applications dominated by numerical calculations.

In general, you should use primitives wherever you need them, unless you discover that you need object support,using Object wrappers is generics where you have to use some Class. For example such collections as List or Map.