Sunday, May 1, 2011

Immutable Objects / Wrapper Class Caching

Since Java 5, wrapper class caching was introduced.
The following is an examination of the cache created by an inner class, IntegerCache, located in the Integer cache. For example, the following code will create a cache:
Integer myNumber = 10; or Integer myNumber = Integer.valueOf(10);

256 Integer objects are created in the range of -128 to 127 which are all stored in an Integer array. This caching functionality can be seen by looking at the inner class, IntegerCache, which is found in Integer:
private static class IntegerCache 
private IntegerCache(){}

static final Integer cache[] = new Integer[-(-128) + 127 + 1];

   for(int i = 0; i < cache.length; i++)
   cache[i] = new Integer(i - 128); 

public static Integer valueOf(int i) 
   final int offset = 128;
   if (i >= -128 && i <= 127) // must cache 
      return IntegerCache.cache[i + offset];
   return new Integer(i);

So when creating an object using Integer.valueOf or directly assigning a value to an Integer within the range of -128 to 127 the same object will be returned. Therefore, consider the following example:

Integer i = 100;
Integer p = 100;
if (i == p)  
System.out.println("i and p are the same.");
if (i != p)   
System.out.println("i and p are different.");    
System.out.println("i and p contain the same value.");

The output is:
i and p are the same.
i and p contain the same value.

So this result is similar to the result as shown here for strings.

It is important to note that object i and p only equate to true because they are the same object, the comparison is not based on the value, it is based on object equality. If Integer i and p are outside the range of -128 or 127 the cache is not used, therefore new objects are created. When doing a comparison for value always use the “.equals” method. It is also important to note that instantiating an Integer does not create this caching. So consider the following example:

Integer i = new Integer (100);
Integer p = new Integer(100);
System.out.println(“i and p are the same object”);
System.out.println(“ i and p contain the same value”);

In this circumstance, the output is only: i and p contain the same value

The other wrapper classes (Byte, Short, Long, Character) also contain this caching mechanism OR constant pooling mechanism. The Byte, Short and Long all contain the same caching principle to the Integer object. The Character class caches from 0 to 127. The negative cache is not created for the Character wrapper as these values do not represent a corresponding character. There is no caching for the Float object.

BigDecimal also uses caching but uses a different mechanism. While the other objects contain a inner class to deal with caching this is not true for BigDecimal, the caching is pre-defined in a static array and only covers 11 numbers, 0 to 10:

// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
new BigDecimal(BigInteger.ZERO,        0,  0),
new BigDecimal(BigInteger.ONE,        1,  0),
new BigDecimal(BigInteger.valueOf(2),    2,  0),
new BigDecimal(BigInteger.valueOf(3),    3,  0),
new BigDecimal(BigInteger.valueOf(4),    4,  0),
new BigDecimal(BigInteger.valueOf(5),    5,  0),
new BigDecimal(BigInteger.valueOf(6),    6,  0),
new BigDecimal(BigInteger.valueOf(7),    7,  0),
new BigDecimal(BigInteger.valueOf(8),    8,  0),
new BigDecimal(BigInteger.valueOf(9),    9,  0),
new BigDecimal(BigInteger.TEN,        10, 0),

As per Java Language Specification(JLS) the values discussed above are stored as immutable wrapper objects. This caching has been created because it is assumed these values / objects are used more frequently.

No comments:

Post a Comment