Why Aren't Integers Cached in Java

Why aren't Integers cached in Java?

It should be very clear that caching has an unacceptable performance hit -- an extra if statement and memory lookup every time you create an Integer. That alone overshadows any other reason and the rest of the agonizing on this thread.

As far as responding "correctly" to ==, the OP is mistaken in his assumption of correctness. Integers DO respond correctly to == by the general Java community's expectation of correctness and of course by the specification's definition of correctness. That is, if two references point to the same object, they are ==. If two references point to different objects, they are not == even if they have the same contents. Thus, it should be no surprise that new Integer(5) == new Integer(5) evaluates to false.

The more interesting question is why new Object(); should be required to create a unique instance every time? i. e. why is new Object(); not allowed to cache? The answer is the wait(...) and notify(...) calls. Caching new Object()s would incorrectly cause threads to synchronize with each other when they shouldn't.

If it were not for that, then Java implementations could totally cache new Object()s with a singleton.

And that should explain why new Integer(5) done 7 times must be required to create 7 unique Integer objects each containing the value 5 (because Integer extends Object).


Secondary, Less Important Stuff: One problem in this otherwise nice scheme results from the autoboxing and autounboxing feature. Without the feature you could not do comparisons such as new Integer(5) == 5. To enable these, Java unboxes the object (and does not box the primitive). Therefore new Integer(5) == 5 is converted to: new Integer(5).intValue() == 5 (and not new Integer(5) == new Integer(5).

One last thing to understand is that autoboxing of n is not done by new Integer(n). It is done internally by a call to Integer.valueOf(n).

If you think you understand and want to test yourself, predict the output of the following program:

public class Foo {
public static void main (String[] args) {
System.out.println(Integer.valueOf(5000) == Integer.valueOf(5000));
System.out.println(Integer.valueOf(5000) == new Integer(5000));
System.out.println(Integer.valueOf(5000) == 5000);
System.out.println(new Integer(5000) == Integer.valueOf(5000));
System.out.println(new Integer(5000) == new Integer(5000));
System.out.println(new Integer(5000) == 5000);
System.out.println(5000 == Integer.valueOf(5000));
System.out.println(5000 == new Integer(5000));
System.out.println(5000 == 5000);
System.out.println("=====");
System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
System.out.println(Integer.valueOf(5) == new Integer(5));
System.out.println(Integer.valueOf(5) == 5);
System.out.println(new Integer(5) == Integer.valueOf(5));
System.out.println(new Integer(5) == new Integer(5));
System.out.println(new Integer(5) == 5);
System.out.println(5 == Integer.valueOf(5));
System.out.println(5 == new Integer(5));
System.out.println(5 == 5);
System.out.println("=====");
test(5000, 5000);
test(5, 5);
}
public static void test (Integer a, Integer b) {
System.out.println(a == b);
}
}

For extra credit, also predict the output if all the == are changed to .equals(...)

Update: Thanks to comment from user @sactiw : "default range of cache is -128 to 127 and java 1.6 onward you can reset the upper value >=127 by passing -XX:AutoBoxCacheMax= from command line"

Integer caching in Java with new operator

Explanation

When you compare Integer vs int with ==, it needs to convert the Integer to an int. This is called unboxing.

See JLS§5.1.8:

If r is a reference of type Integer, then unboxing conversion converts r into r.intValue()

At that point, you are comparing int vs int. And primitives have no notion of instances, they all refer to the same value. As such, the result is true.

So the actual code you have is

a.intValue() == c

leading to a comparison of 10 == 10, both int values, no Integer instances anymore.

You can see that new Integer(...) indeed creates new instances, when you compare Integer vs Integer. You did that in a == b.


Note

The constructor new Integer(...) is deprecated. You should instead use Integer#valueOf, it is potentially faster and also uses an internal cache. From the documentation:

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

The caching is important to note here, since it yields to == being true again (for cached values):

Integer first = Integer.valueOf(10);
Integer second = Integer.valueOf(10);
System.out.println(first == second); // true

The caching is guaranteed for values between -128 and +127, but may also be used for others.

Also note that your b actually comes out of the cache, since

Integer b = 10;
// same as
Integer b = Integer.valueOf(10);
// and not
Integer b = new Integer(10);

So boxing goes through Integers cache (see JLS§5.1.7).

Caching of boxed values in Java

Because these assignments are made using the 'new' keyword, they are new instances created, and not picked up from the pool. Also note that the integer pool contains only integers from -128 to 127. For higher values, pool do not come to play.

Integer third = 4;
Integer fourth = 4;
Integer fifth = 130;
Integer sixth = 130;
Integer seventh = 127;
Integer eighth = 127;
System.out.println(third == fourth); // 4==4 Return true
System.out.println(fifth == sixth); // 130==130 Returns false
System.out.println(seventh == eighth); // 127==127 Returns true

edit
As rightly mentioned in the comment, from Java 1.6 auto box limit can be extended by using -XX:AutoBoxCacheMax=new_limit from command line.

How/why is Integer caching faster in Java?

The performance benefit is nothing to do with ==. The real reason is that the cache allows valueOf to avoid creating lots of objects when the same "small" integers are boxed repeatedly. Integer objects occupy space (at least 16 bytes). Creating lots of them means that the garbage collector needs to run more frequently.

My understanding is that the Java team did a lot of analysis of real-world applications, and came to the conclusion that the Integer cache was a worthwhile optimization.

(The reason I say that it is nothing to do with == is that you cannot rely on == working for Integer objects. Therefore, any code that does this ... without also ensuring that the integers are in the specified range ... is buggy. And if you do a range check to ensure that the numbers are in-range, you are likely to spend more on that than you save by using ==.)


"... which also leads to faster code due to better cache efficiency." How does it lead to faster code?

The cache efficiency they are talking about is the hardware level memory cache that most modern processors use to deal with the mismatch between CPU and memory speeds. If the application has only one Integer object representing the number 1 and that number appears frequently, then the chances that the memory addresses holding the 1 object are in the (memory) caches are increased. Cache hits means faster code.


Does it have anything to do with the following method found in the IntegerCache class?

Erm ... yes.

Why Java does not see that Integers are equal?

Check out this article: Boxed values and equality

When comparing wrapper types such as Integers, Longs or Booleans using == or !=, you're comparing them as references, not as values.

If two variables point at different objects, they will not == each other, even if the objects represent the same value.

Example: Comparing different Integer objects using == and !=.

Integer i = new Integer(10);
Integer j = new Integer(10);
System.out.println(i == j); // false
System.out.println(i != j); // true

The solution is to compare the values using .equals()

Example: Compare objects using .equals(…)

Integer i = new Integer(10);
Integer j = new Integer(10);
System.out.println(i.equals(j)); // true

…or to unbox the operands explicitly.

Example: Force unboxing by casting:

Integer i = new Integer(10);
Integer j = new Integer(10);
System.out.println((int) i == (int) j); // true


References / further reading

  • Java: Boxed values and equality
  • Java: Primitives vs Objects and References
  • Java: Wrapper Types
  • Java: Autoboxing and unboxing

Why aren't floating point objects equal in Java when integer objects are?

Your method isEqual() expects Objects as arguments. So the primitive longs and floats are boxed, and the created Long/Float instances are passed as argument. This internally calls the methods Long.valueOf()/Float.valueOf().

Your implementation of Long.valueOf() caches some values and returns cached values (it's allowed, but not required to do so, unlike Integer.valueOf() which is required to return cached values for the range -128 to 127)). So Long.valueOf(22L) == Long.valueOf(22L). That's just not the case with Float.valueOf().

Try with larger long values, and the comparison will be false.

You should never compare objects using ==, except if they are enums. Use equals().

Why does variables of class Test for two different objects gives true for == operation and same operation results in false for String Objects?

strings are normally cached in java, so two strings with the same value might have the same reference. (The same goes for Integers, there objects in a certain range are referenced as the same object, if they have the same value). This could lead to having the same object "A" as your value of s for t1 and t2 in the constructors. Two int primitives are always the same if they have the same value.

Java Integer auto auto-boxing

Is this similar to String internalization?

Yes - essentially all integers that can fit in a byte (-128 to +127) are interned and thus share the same underlying object. Larger ones aren't, and thus probably don't share the same underlying object (this is covered in JLS 5.1.7) - though note that there's nothing in the spec that prevents larger integers sharing the same underlying object if someone were to choose to implement a VM that way.

I would imagine the rationale is that "smaller" integers in this range are used much more often than larger ones, so using the same underlying objects is worth it to reduce the potential memory footprint.

In your new Integer(100) == new Integer(100) example, this isn't the case since you're explicitly creating new integer objects, similar to how new String("hi") == new String("hi") evaluates to false.

Just to re-iterate - when comparing integers like this in all real world scenarios, then .equals() should be used (or preferably still, == with primitive integers unless there's a good case for using the object type.)



Related Topics



Leave a reply



Submit