In Herbert Schildt's "Java: The Complete Reference, 9th Edition", there's an example that kinda baffles me. Its key point that i can't comprehend could be summed up to the following code:
class Test {
public static void main (String args[]) {
Integer i1 = 6;
Integer i2 = 6;
Integer i3 = 6;
Integer i4 = (args.length + 1) * 6;
if (i1 == i2) System.out.println("WTF");
if (i3 == i4) System.out.println("Super WTF!!!");
}
}
To my surprise, the result of compiling and executing such code using JDK 8 update 40 is as follows:
WTF
Super WTF!!!
All the Java books, manuals and other informational resources that i've seen state that the equality operator (==), when used to compare objects, simply matches their reference values. So, if two object variables refer to distinct instances, then the == operator returns false, even if the internal contents of those objects are the same. Which is quite different from the behavior expressed by the example above.
While checking SO for similar questions, i found a somewhat related one, which is about comparing String objects using ==. There, an interesting feature of Java called interning was mentioned. Apparently, all the string literals and compile-time string constants are sort of "cached" by JVM, so that, for example, multiple String references initialized with the same string literals actually point to the same object.
But here we deal with numeric values and Integer objects. Also, one of the variables, i4, is initialized using a number of command line arguments supplied to the program, which is definitely a run-time information. Yet, == still finds it equal to i3.
So, given all the above, how exactly is the equality operator supposed to work in Java? Does it, or does it not, inspect the objects' contents when comparing them?
Thanks!
Integer objects are cached as mentioned by the Javadocs of Integer#valueOf(int):
This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
The statement Integer i4 = (args.length + 1) * 6; calls Integer#valueOf(int) which returns the cached instance. On the other hand, if you use Integer i4 = new Integer((args.length + 1) * 6);, the reference equality will return false.
I think the example shows us how java int pool is working. So in the i4 case if you provided no arguments on runtime the result is 6. Now the int pool kicks in and uses the i1 value which is part of the -128 to to 127 int pool. So on an object level they really equal and that's what == is all about.
When you autobox an int, you're internally calling Integer.valueOf(int), which according to the documentation may return cached values. So it makes perfect sense that you're seeing reference equality.
More generally, you should never assume two references are distinct unless you explicitly constructed them. And for the record, string interning is available at runtime as well, through the String.intern() method.
Related
As of Java 1.5, you can pretty much interchange Integer with int in many situations.
However, I found a potential defect in my code that surprised me a bit.
The following code:
Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
mismatch = true;
appeared to be incorrectly setting mismatch when the values were equal, although I can't determine under what circumstances. I set a breakpoint in Eclipse and saw that the Integer values were both 137, and I inspected the boolean expression and it said it was false, but when I stepped over it, it was setting mismatch to true.
Changing the conditional to:
if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))
fixed the problem.
Can anyone shed some light on why this happened? So far, I have only seen the behavior on my localhost on my own PC. In this particular case, the code successfully made it past about 20 comparisons, but failed on 2. The problem was consistently reproducible.
If it is a prevalent problem, it should be causing errors on our other environments (dev and test), but so far, no one has reported the problem after hundreds of tests executing this code snippet.
Is it still not legitimate to use == to compare two Integer values?
In addition to all the fine answers below, the following stackoverflow link has quite a bit of additional information. It actually would have answered my original question, but because I didn't mention autoboxing in my question, it didn't show up in the selected suggestions:
Why can't the compiler/JVM just make autoboxing “just work”?
The JVM is caching Integer values. Hence the comparison with == only works for numbers between -128 and 127.
Refer: #Immutable_Objects_.2F_Wrapper_Class_Caching
You can't compare two Integer with a simple == they're objects so most of the time references won't be the same.
There is a trick, with Integer between -128 and 127, references will be the same as autoboxing uses Integer.valueOf() which caches small integers.
If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.
Resources :
JLS - Boxing
On the same topic :
autoboxing vs manual boxing java
"==" always compare the memory location or object references of the values. equals method always compare the values. But equals also indirectly uses the "==" operator to compare the values.
Integer uses Integer cache to store the values from -128 to +127. If == operator is used to check for any values between -128 to 127 then it returns true. for other than these values it returns false .
Refer the link for some additional info
Integer refers to the reference, that is, when comparing references you're comparing if they point to the same object, not value. Hence, the issue you're seeing. The reason it works so well with plain int types is that it unboxes the value contained by the Integer.
May I add that if you're doing what you're doing, why have the if statement to begin with?
mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
The issue is that your two Integer objects are just that, objects. They do not match because you are comparing your two object references, not the values within. Obviously .equals is overridden to provide a value comparison as opposed to an object reference comparison.
Besides these given great answers, What I have learned is that:
NEVER compare objects with == unless you intend to be comparing them
by their references.
As well for correctness of using == you can just unbox one of compared Integer values before doing == comparison, like:
if ( firstInteger.intValue() == secondInteger ) {..
The second will be auto unboxed (of course you have to check for nulls first).
To create a new Integer object that holds the value in java 1 which one of the following is right and what exactly is the difference in the following methods as all print the value?
Method 1 :
Integer p = new Integer(1);
Method 2 :
Integer p = 1;
Method 3 :
Integer p = new Integer("1");
Using method three I got the following warning :
Note: HelloWorld.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details
You skipped the intended solution:
Integer p = Integer.valueOf(1);
This pattern is known as Factory method pattern. One may ask what the benefit of this method is. Luckily, the implementation of class Integer is open-source, so let's take a look:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
There seems to be some kind of Integer-value cache. If one requests an Integer with a value within the cache-range, Java does not create a new object, but returns a previously created one. This works because Integers are immutable. One can even control the upper cache limit with the system property java.lang.Integer.IntegerCache.high=....
And why do the other two methods of creating an Integer generate a warning? Because they were set deprecated with Java 9.
Integer#Integer(int value):
Deprecated. It is rarely appropriate to use this constructor. The static factory valueOf(int) is generally a better choice, as it is likely to yield significantly better space and time performance. [...]
Integer#Integer(String s):
Deprecated. It is rarely appropriate to use this constructor. Use parseInt(String) to convert a string to a int primitive, or use valueOf(String) to convert a string to an Integer object. [...]
And just for completeness, here is the part for Integer.valueOf(int i):
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.
EDIT 1: Thanks to #VGR mentioning that
Integer p = 1;
is equivilant to
Integer p = Integer.valueOf(1);
This, however, is only true for int-values between -128 and 127. The behaviour is defined in JLS §5.1.7:
[...] If the value p being boxed is the result of evaluating a constant expression (§15.28) of type boolean, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
EDIT 2: Thanks to #DorianGray, who brought the following to my attention.
While not in the JLS, the version of javac I am using (9.0.4) does compile the boxing down to Integer.valueOf(...); as it is shown in this answer by Adam Rosenfield.
Method 4, Integer p = Integer.valueOf(1); is the recommended way. The JavaDoc says:
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.
I am using Java ArrayList.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(-129);
list.add(-129);
return (list.get(0) == list.get(1));
The return value is false.
I know it must be some range problem since when I use -128, it returns true.
But could someone tell me the reason of it?
Java caches integers in the range -128..127 (signed bytes). It saves a lot of allocations of tiny numbers which is very common in lots of code. If you use equals() instead of ==, it will work. The == check is comparing that two int types, that have been magically boxed (i.e. converted) to Integer are the same reference - which they are not. But int in the signed byte range will be the same. The equals check will actually compare the value of the variables:
return (list.get(0).equals(list.get(1)));
When you perform:
list.add(-129);
list.add(-129);
It create two separate object with separate memory location.when you perform == it return false.
In Java, values from -128 to 127 are cached, so the same objects are returned and you will get true.
For more information see:http://www.geeksforgeeks.org/comparison-autoboxed-integer-objects-java/
If two references point to two different objects, then in accordance with the "= =" to judge both is unequal, even though both have the same reference content. As we know, if two references refer to the same object, in accordance with the "= =" to judge the two are equal.
If you go to the Integer. Java classes, you will find that there is a internal private class IntegerCache. Java, it caches from all the Integer object - between 128 ~ 127.
So the thing is, all the small integers are in the internal cache.
If the value is between 128 ~ 127, then it will be returned from a cache instance,point to the same object.
I have this question,
vectors in java only accept objects right? but this piece of code compiles just fine
Vector myv=new Vector();
myv.addElement(1);
myv.addElement(1);
does it encapsulate it as an object of Integer? and if it does, why do this statement
System.out.println(myv.elementAt(0)==myv.elementAt(1));
gives true ?!!! they're supposed to be two distinct objects...?
at the same time this statement throws an error, which ensures that the element is an object.
int x=myv.elementAt(0);
can someone explain this for me? thank you.
The 1 is auto-boxed into an Integer.
To learn more about Autoboxing and -unboxing, see Oracle's Docu: https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
On why they are the same: Autoboxing ints to Integers uses a cache. For numbers < 128 (I think) the same Integer-Object will be returned each time instead of a new one being created. So your code essentially inserts the same object twice into the Vector, and for this reason the identity comparison using == returns true.
As you don't use generics with your Vector, myv.elementAt(0) will return an Object, which cannot be cast to an int. If you used generics, i.e. Vector<Integer> myv = new Vector<>(), then elementAt would return an Integer which would be auto-unboxed to an int.
That works because java auto boxes and auto unboxes these type for you.
But note that this doesn't work for sure in every scenario.
Let's take this example:
Integer i = 1234;
Integer z = 1234;
System.out.println("i: "+i);
System.out.println("z: "+z);
System.out.println("== : "+(i == z));
System.out.println("equals: "+i.equals(z));
It will produce the following output:
i: 1234
z: 1234
== : false
equals: true
DEMO
In fact, from this answer you can see that it works only for integers between -128 and +127
In fact, this behaves as expected:
Integer i = 123;
Integer z = 123;
System.out.println("i: "+i);
System.out.println("z: "+z);
System.out.println("== : "+(i == z));
System.out.println("equals: "+i.equals(z));
and produces:
i: 123
z: 123
== : true
equals: true
DEMO
Here is something pretty funny about Java, it caches integers between -128 and 127 (for auto-/un-boxing) and any reference to integers in that range all refer to the same object (since == here is comparing Objects). This can cause quite a few bugs when people don't thing through the == method comparing Objects, rather than values.
So you will get true for comparing "integers" (autoboxed) between -128 - 127. If you do the same thing with 128, it'll say false.
Check it out:
import java.util.Vector;
public class Vect {
public static void main(String[] args) {
Vector v = new Vector();
v.addElement(127);
v.addElement(127);
v.addElement(128);
v.addElement(128);
System.out.println(v.elementAt(0) == v.elementAt(1));
System.out.println(v.elementAt(2) == v.elementAt(3));
}
}
Will give you:
$ java Vect
true
false
This is called autoboxing. When the Java compiler encounters an integer, float or double value in a place where an object is expected, it automatically creates a wrapper object.
The code generated by the compiler for your code is equivalent to
Vector myv=new Vector();
myv.addElement(Integer.valueOf(1));
myv.addElement(Integer.valueOf(1));
The valueOf() method caches Integer instances for the values from -128 to 127 (IIRC), with the result that both values will be replaced by the same object.
Try the same with a lager value, like 1000. Then the objects will be different.
This is a feature of compiled code and auto-boxing.
Generics in Java are a compile-time issue. javac uses them to check that you use the types correctly, but the compiled byte code that's produced doesn't know anything about the generic types. Generics are implemented with type erasure in Java was mainly for backward compatibility. (That's also why raw types still exist in Java.)
It means that an Vector<E> will look like a plain, raw Vector at runtime - it's just a plain Vector that contains objects. Since primitives are not objects, you can't store primitive types in such an Vector.
There are wrapper classes for each of the primitive types (int -> Integer, long -> Long, short -> Short, boolean --> Boolean, etc.) so is possible to make it such that a Vector<int> would use auto-boxing to store ints in Integer objects, which could be stored in a Vector. ...but the designers of the Java language didn't decide to take auto-boxing that far.
Java's Vector is a generics, it means it can accept any kind of data to store. You have to specify the type of data to hold with a syntax like Vector<String>. Replace String for the kind of object you want the Vector to hold.
The reason that it gives true is because it is autoboxed into the Integer class automatically; when compares two Integers, it does the same way as the built-in type int.
EDIT: The autoboxing comparation works with the int values in the range [-128;+127].
This question already has answers here:
Weird Integer boxing in Java
(12 answers)
Closed 8 years ago.
Take a look at following code:
Long minima = -9223372036854775808L;
Long anotherminima = -9223372036854775808L;
System.out.println(minima==anotherminima); //evaluates to false
System.out.println(Long.MIN_VALUE);
Long another= 1L;
Long one = 1L;
System.out.println(another == one); //evaluates to true
I am not able to understand this behavior..? I was hoping the first eval to be true as well..
And that is what I am expecting..
There is a caching technique used by the JVM for some range of autoboxing value. As specified in the spec ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.7 )
If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.
Ideally, boxing a given primitive value p, would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rules above are a pragmatic compromise. The final clause above requires that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, this formulation disallows any assumptions about the identity of the boxed values on the programmer's part. This would allow (but not require) sharing of some or all of these references.
This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all char and short values, as well as int and long values in the range of -32K to +32K.
So 1L is in this cached values and then the autoboxing give the exact same reference, but in the case of the number outside of this range (like the Long.MIN_VALUE) it isn'T cached and thus a different instance/reference is given.
In any case, when comparing object you should always use .equals()
Just a guess here, but it could be that 1L is in the constant pool, and thus the reference equality evaluates to true (just like how sometimes, even by Strings, == will evaluate to true), while that other huge number isn't. Not sure how to check which constants are in the pool at initiation.
Edit: Java has a cache of certain constant objects (including the wrapper classes for primitives, and String). Thus, if you write
String st1 = "A";
if "A" is in the constant pool, Java won't create a new String object- it will just create a reference to the already existing one. So if you then did
String st2 = "A";
System.out.println(st1 == st2);
It would print out true.
Now, not all Integer, Long, Short, etc... are cached (there are way too many), but the lower values are. So I would assume that 1L is. That means that in your question, both another and one refer to the same object, and thus it returns true even for reference equality.
First of all you should use long instead of Long. Secondly == between Integer, Long etc will check for reference equality. You may check the 5.1.7 Boxing Conversion. Alo 1L is in the constant pool so the second case is returning true.
On a side note you should use .equals for comparing the long.
From the Oracle docs:
If the value p being boxed is true, false, a byte, or a char in the
range \u0000 to \u007f, or an int or short number between -128 and 127
(inclusive), then let r1 and r2 be the results of any two boxing
conversions of p. It is always the case that r1 == r2.
Ideally, boxing a given primitive value p, would always yield an
identical reference. In practice, this may not be feasible using
existing implementation techniques. The rules above are a pragmatic
compromise. The final clause above requires that certain common values
always be boxed into indistinguishable objects. [...]
This ensures that in most common cases, the behavior will be the
desired one, without imposing an undue performance penalty, especially
on small devices. Less memory-limited implementations might, for
example, cache all char and short values, as well as int and long
values in the range of -32K to +32K.
Adding on to this answer, everything from -128 to 127 is cached into an instance in permanent java memory, meaning that those numbers (almost) always have reference equality, because new instances are not created for them. Outside of the cache, a new instance is created every time the number is used, so they are not equal referentially. Read more here and here
problem:
(minima==anotherminima)
You are comparing the memory location of the object not its values thus it returns false.
If you want to compare two long wrapper class you need to call compareTo
From jls
If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f,
or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results
of any two boxing conversions of p. It is always the case that r1 == r2.
Now if you use -128 and 127 to initialized the Long wrapper class it will result to boxing conversion which will have the same reference.
You should use long instead of Long. Note that Long is a class and so you must use equals() to compare instances of it. On the other hand, if you use long instead, you can compare with == because these are primitives.