Java: liczba całkowita równa się vs. ==

152

Począwszy od Javy 1.5, można Integerz niej korzystać intw wielu sytuacjach.

Jednak znalazłem potencjalną wadę w moim kodzie, która trochę mnie zaskoczyła.

Poniższy kod:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

wydawało się, że ustawienie niezgodności jest nieprawidłowe, gdy wartości były równe, chociaż nie mogę określić, w jakich okolicznościach. Ustawiłem punkt przerwania w Eclipse i zobaczyłem, że Integerwartości były równe 137, i sprawdziłem wyrażenie logiczne i stwierdziłem, że jest fałszywe, ale kiedy go przekroczyłem, ustawiało niezgodność na true.

Zmiana warunku na:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

naprawiono problem.

Czy ktoś może rzucić trochę światła na to, dlaczego tak się stało? Jak dotąd widziałem zachowanie tylko na moim hoście lokalnym na moim własnym komputerze. W tym konkretnym przypadku kod z powodzeniem pokonał około 20 porównań, ale zawiódł na 2. Problem był powtarzalny.

Jeśli jest to powszechny problem, powinien powodować błędy w innych naszych środowiskach (deweloperskich i testowych), ale jak dotąd nikt nie zgłosił problemu po setkach testów wykonujących ten fragment kodu.

Czy nadal nie jest to zgodne z prawem == porównywanie dwóch Integerwartości?

Oprócz wszystkich dobrych odpowiedzi poniżej, poniższy link stackoverflow zawiera sporo dodatkowych informacji. Właściwie odpowiedziałby na moje pierwotne pytanie, ale ponieważ nie wspomniałem w moim pytaniu o autoboxingu, nie pojawił się w wybranych sugestiach:

Dlaczego kompilator / JVM nie może po prostu sprawić, by autoboxing „po prostu działał”?

Jeremy Goodell
źródło

Odpowiedzi:

238

JVM buforuje wartości całkowite. == działa tylko dla liczb od -128 do 127 http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching

Adam
źródło
1
Dzięki, to z pewnością wyjaśnia, dlaczego 137 zawodzi! Odpowiada również na moje pytanie, dlaczego nie jest to powszechny problem. W 95% przypadków, z którymi się spotkam, wartość będzie niższa niż 127. Dobrze to teraz złapać dla 5%, gdzie tak nie jest.
Jeremy Goodell,
1
Interesująca uwaga na marginesie: jeszcze kilka tygodni temu cdiCt i cdsCt były intami, więc było w porządku, ale musiałem uczynić je liczbami całkowitymi, aby sprawdzić sytuację zerową, która jest traktowana inaczej ...
Jeremy Goodell
3
@Jeremy Tak, to dość niejasny problem, ale generalnie używasz .equals () dla obiektów i == dla prymitywów. Nie możesz polegać na autounboxingu do testowania równości.
Adam,
1
Lol, zaznacz ponownie do siebie! Wygląda na to, że Colin i tak ma już wystarczająco dużo punktów.
Jeremy Goodell
2
Zauważ, że nowa Integer (1)! = Nowa Integer (1). new ZAWSZE zwraca nowy adres. Autoboxing używa wersji z pamięci podręcznej. Inne sposoby zwracania liczb całkowitych (bez ich tworzenia) prawdopodobnie zwracają również wartość przechowywaną w pamięci podręcznej.
Bill K
77

Nie możesz porównać dwóch Integerz prostymi ==obiektami to, więc przez większość czasu odniesienia nie będą takie same.

Istnieje sztuczka, Integermiędzy -128 a 127 referencje będą takie same, jak w przypadku autoboxingu, Integer.valueOf()który buforuje małe liczby całkowite.

Jeśli wartość p w ramce to prawda, fałsz, bajt, znak w zakresie od \ u0000 do \ u007f lub liczba int lub krótka między -128 a 127, to niech r1 i r2 będą wynikiem dowolnych dwóch konwersji typu box z str. Zawsze jest tak, że r1 == r2.


Zasoby :

Na ten sam temat:

Colin Hebert
źródło
1
Czy gwarancja jest udzielona przez JLS czy tylko dla Oracle JVM?
Thorbjørn Ravn Andersen
Cytowana część pochodzi z JLS, więc jest to gwarancja JLS
Colin Hebert
Re: gwarancja. Nadal nie polegałbym na tym zbytnio. new Integer(1) == new Integer(1)jest nadal fałszywa.
Thilo
@Thilo new ... == new ...jest zawsze false.
MC Emperor
2
@Thilo Prawda, zawsze używaj w equals()przypadku obiektów. Powinna to być jedna z pierwszych rzeczy, które należy wiedzieć podczas nauki języka Java. Nawiasem mówiąc, domyśliłbym się, że konstruktor Integerbył prywatny, tj. Że instancje zawsze były tworzone za pomocą valueOf()metody. Ale widzę, że konstruktor jest publiczny.
MC Emperor
5

Problem polega na tym, że twoje dwa obiekty typu Integer są po prostu obiektami. Nie pasują, ponieważ porównujesz dwa odwołania do obiektów, a nie wartości w nich zawarte. Oczywiście .equalsjest zastępowany, aby zapewnić porównanie wartości, a nie porównanie odniesień do obiektów.

MattC
źródło
Dobra odpowiedź, ale nie wyjaśnia, dlaczego zawodzi tylko dla 137.
Jeremy Goodell
4

Integerodnosi się do odniesienia, to znaczy podczas porównywania odniesień, które porównujesz, jeśli wskazują na ten sam obiekt, a nie wartość. Stąd problem, który widzisz. Powodem, dla którego działa tak dobrze z inttypami zwykłymi, jest to, że rozpakowuje wartość zawartą w Integer.

Mogę dodać, że jeśli robisz to, co robisz, to po co miałbyś ifzacząć od stwierdzenia?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
pszenicy
źródło
4

„==” zawsze porównuje lokalizację pamięci lub odniesienia do obiektów wartości. equals zawsze porównuje wartości. Ale equals pośrednio używa operatora „==” do porównywania wartości.

Integer używa pamięci podręcznej Integer do przechowywania wartości od -128 do +127. Jeśli operator == jest używany do sprawdzania wartości od -128 do 127, zwraca prawdę. dla innych niż te wartości zwraca false.

Skorzystaj z łącza, aby uzyskać dodatkowe informacje

vijay
źródło
1

Aby uzyskać poprawność użycia ==, możesz po prostu rozpakować jedną z porównywanych Integerwartości przed wykonaniem ==porównania, na przykład:

if ( firstInteger.intValue() == secondInteger ) {..

Drugi zostanie automatycznie rozpakowany (oczywiście musisz nullnajpierw sprawdzić ).

Mc Bton
źródło
0

Oprócz tych wspaniałych odpowiedzi, dowiedziałem się, że:

NIGDY nie porównuj obiektów za pomocą ==, chyba że zamierzasz porównywać je przez odniesienia.

ZhaoGang
źródło