Dlaczego porównania == z Integer.valueOf (String) dają różne wyniki dla 127 i 128?

182

Nie mam pojęcia, dlaczego te wiersze kodu zwracają różne wartości:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

Dane wyjściowe to:

true
false
true

Dlaczego pierwszy powraca, truea drugi powraca false? Czy jest coś innego, że nie wiem, od 127a 128? (Oczywiście, że wiem, że 127< 128.)

Ponadto, dlaczego trzeci powraca true?

Przeczytałem odpowiedź na to pytanie , ale wciąż nie wiedziałem, jak można ją zwrócić truei dlaczego zwraca kod w drugiej linii false.

DnR
źródło
6
Liczba całkowita jest obiektem; jeśli chcesz porównać na równość, użyj .equals(), w przeciwnym razie wszystkie zakłady są wyłączone.
Karl Damgaard Asmussen
6
@KarlDamgaardAsmussen Właściwie tutaj naprawdę chcę sprawdzić, czy są to odniesienia do tego samego obiektu, i na początku nie rozumiem, dlaczego 127 128 zwraca inny wynik.
DnR
@DnR, jeśli Java byłaby językiem o znormalizowanej specyfikacji, pomyślałabym, że pozwala na takie kwestie aż do wdrożenia, a nawet nakazuje niezdefiniowane zachowanie.
Karl Damgaard Asmussen
1
@jszumski: W tym pytaniu jest coś więcej niż tylko pamięć podręczna. Poza tym połączona odpowiedź jest co najwyżej niekompletna - nie dość szczegółowo opisuje, co jest buforowane i dlaczego.
Makoto
1
Aby uzyskać dalsze informacje na temat tej dyskusji, zapoznaj się z tym postem .
Jeroen Vannevel

Odpowiedzi:

191

Jest tutaj uderzająca różnica.

valueOfzwraca Integerobiekt, którego wartości mogą być buforowane między -128 a 127. Dlatego pierwsza wartość zwraca true- jest buforowana - a druga wartość false- 128 nie jest wartością buforowaną, więc otrzymujesz dwa osobne Integerwystąpienia .

Ważne jest, aby pamiętać , że porównujesz odniesienia Integer#valueOf, a jeśli porównujesz wartość, która jest większa niż obsługiwana przez pamięć podręczną, nie oceni tego true, nawet jeśli przeanalizowane wartości są równoważne (przypadek:) Integer.valueOf(128) == Integer.valueOf(128). Państwo musi użyć equals()zamiast.

parseIntzwraca prymityw int. To dlatego zwraca wartość trzecie true- 128 == 128jest oceniany, a to oczywiście true.

Teraz zdarza się sporo, aby uzyskać ten trzeci wynik true:

  • Konwersja rozpakowywania występuje w odniesieniu do operatora równoważności, którego używasz, oraz typów danych, które masz - a mianowicie inti Integer. Oczywiście dostajesz Integerz valueOfprawej strony.

  • Po konwersji porównujesz dwie prymitywne intwartości. Porównanie odbywa się tak, jak można by się tego spodziewać w odniesieniu do prymitywów, więc kończy się porównywanie 128i 128.

Makoto
źródło
2
@ user3152527: Istnieje spora różnica - jeden jest uważany za obiekt, co oznacza, że ​​możesz wywoływać metody i wchodzić z nim w interakcje w abstrakcyjne struktury danych, takie jak List. Drugi to prymitywny, który jest tylko surową wartością.
Makoto
1
@ user3152527 Zadałeś doskonałe pytanie (w najgorszym przypadku nie głupie). Ale naprawiłeś to, żeby używać .equals, prawda?
user2910265
3
Ach, wygląda na to, że pytający nie zrozumiał podstawowego faktu w Javie: Kiedy używasz „==” do porównywania dwóch obiektów, testujesz, czy są to odniesienia do tego samego obiektu. Korzystając z „equals ()”, sprawdzasz, czy mają taką samą wartość. Nie można użyć „równa się” do porównania prymitywów.
Jay
3
@ Jay nie, rozumiem to. ale to, co mnie na początku myli, to dlaczego pierwszy zwraca true, a drugi false za pomocą tej samej metody porównania ==. zresztą teraz jest jasne.
DnR
1
Nit: nie chodzi tylko o to, że liczba całkowita „może” być buforowana między -128 a 127. Tak musi być, zgodnie z JLS 5.1.7 . To może być buforowane poza tym zakresem, ale nie musi być (i często nie jest).
yshavit
127

IntegerKlasa ma statyczną pamięć, która przechowuje 256 specjalne Integerobiekty - jeden dla każdej wartości pomiędzy -128 i 127. Mając to na uwadze, należy rozważyć różnicę pomiędzy tymi trzema.

new Integer(123);

To (oczywiście) tworzy zupełnie nowy Integerprzedmiot.

Integer.parseInt("123");

Zwraca to intpierwotną wartość po parsowaniu String.

Integer.valueOf("123");

Jest to bardziej złożone niż inne. Zaczyna się od analizy String. Następnie, jeśli wartość wynosi od -128 do 127, zwraca odpowiedni obiekt ze statycznej pamięci podręcznej. Jeśli wartość znajduje się poza tym zakresem, wówczas wywołuje new Integer()i przekazuje wartość, aby uzyskać nowy obiekt.

Teraz rozważ trzy wyrażenia w pytaniu.

Integer.valueOf("127")==Integer.valueOf("127");

Zwraca wartość true, ponieważ Integerwartość tego parametru wynosi 127, która jest dwukrotnie pobierana ze statycznej pamięci podręcznej i porównywana z samym sobą. W grę wchodzi tylko jeden Integerobiekt, więc to się zwraca true.

Integer.valueOf("128")==Integer.valueOf("128");

Zwraca false, ponieważ 128 nie znajduje się w statycznej pamięci podręcznej. Tak więc powstaje nowy Integerdla każdej strony równości. Ponieważ istnieją dwa różne Integerobiekty, a ==dla obiektów zwraca się tylko truewtedy, gdy obie strony są dokładnie tym samym obiektem, tak właśnie będzie false.

Integer.parseInt("128")==Integer.valueOf("128");

Porównuje to pierwotną intwartość 128 po lewej stronie, z nowo utworzonym Integerobiektem po prawej stronie. Ponieważ jednak nie ma sensu porównywać intan Integer, Java automatycznie rozpakuje plik Integerprzed wykonaniem porównania; więc kończysz na porównaniu intdo int. Ponieważ prymityw 128 jest równy sobie, zwraca to true.

Dawood ibn Kareem
źródło
13

Zadbaj o zwrot wartości z tych metod. Metoda valueOf zwraca instancję liczby całkowitej:

public static Integer valueOf(int i)

Metoda parseInt zwraca wartość całkowitą (typ pierwotny):

public static int parseInt(String s) throws NumberFormatException

Objaśnienie do porównania:

Aby zaoszczędzić pamięć, dwa wystąpienia obiektów opakowania zawsze będą wynosić ==, gdy ich pierwotne wartości są takie same:

  • Boolean
  • Bajt
  • Znak od \ u0000 do \ u007f (7f to 127 w systemie dziesiętnym)
  • Krótka i całkowita od -128 do 127

Kiedy == jest używane do porównania prymitywu z opakowaniem, opakowanie zostanie rozpakowane, a porównanie będzie prymitywne z pierwotnym.

W twojej sytuacji (zgodnie z powyższymi zasadami):

Integer.valueOf("127")==Integer.valueOf("127")

To wyrażenie porównuje odwołania do tego samego obiektu, ponieważ zawiera wartość całkowitą z przedziału od -128 do 127, więc zwraca true.

Integer.valueOf("128")==Integer.valueOf("128")

Wyrażenie to porównuje odniesienia do różnych obiektów, ponieważ zawierają wartości całkowite spoza <-128, 127>, więc zwraca false.

Integer.parseInt("128")==Integer.valueOf("128")

To wyrażenie porównuje pierwotną wartość (lewa strona) i odniesienie do obiektu (prawa strona), więc prawa strona zostanie rozpakowana, a jego prymitywny typ zostanie porównany z lewą, więc zwróci true.

piobab
źródło
3
Podobne pytanie: stackoverflow.com/questions/9824053/…
piobab
Czy możesz podać adres URL źródła oferty?
Philzen,
„... dwa wystąpienia obiektów opakowania zawsze będą ==, gdy ich prymitywne wartości będą takie same ...” - absolutnie fałszywe. Jeśli utworzysz dwa obiekty opakowania o tej samej wartości, nie zwrócą wartości true w porównaniu z ==, ponieważ są to różne obiekty.
Dawood ibn Kareem
6

Obiekty całkowite buforują od -128 do 127 z 256 liczb całkowitych

Nie należy porównywać referencji do obiektów z == lub ! = . Powinieneś użyć . równa się (..) zamiast, lub lepiej - użyj pierwotnej int zamiast Integer.

parseInt : Analizuje argument ciągu jako liczbę całkowitą ze znakiem dziesiętnym. Wszystkie znaki w ciągu muszą być cyframi dziesiętnymi, z tym wyjątkiem, że pierwszy znak może być znakiem minus ASCII „-” („\ u002D”), aby wskazać wartość ujemną. Wynikowa wartość całkowita jest zwracana, dokładnie tak, jakby argument i podstawa 10 zostały podane jako argumenty do metody parseInt (java.lang.String, int).

valueOf Zwraca obiekt typu Integer zawierający wartość wyodrębnioną z określonego ciągu po przeanalizowaniu z podstawką podaną przez drugi argument. Pierwszy argument jest interpretowany jako reprezentujący liczbę całkowitą ze znakiem w podstawce podanej przez drugi argument, dokładnie tak, jakby argumenty zostały przekazane metodzie parseInt (java.lang.String, int). Wynikiem jest obiekt typu Integer, który reprezentuje wartość całkowitą określoną przez ciąg.

równoważny

new Integer(Integer.parseInt(s, radix))

radix - podstawa używana przy interpretacji s

więc jeśli jesteś równy Integer.valueOf()liczbie całkowitej między nimi

-128 do 127 zwraca true w twoim stanie

dla lesser than-128 i greater than127 dajefalse

Nambi
źródło
6

Aby uzupełnić podane odpowiedzi, zwróć również uwagę na następujące kwestie:

public class Test { 
    public static void main(String... args) { 
        Integer a = new Integer(129);
        Integer b = new Integer(129);
        System.out.println(a == b);
    }
}

Ten kod wydrukuje również: false

Jak twierdził użytkownik Jay w komentarzu do zaakceptowanej odpowiedzi, należy zachować ostrożność podczas używania operatora ==na obiektach, tutaj sprawdza się, czy oba odniesienia są takie same, co nie jest, ponieważ są one różnymi celami, chociaż reprezentują one bardzo ta sama wartość. Aby porównać obiekty, należy equals zamiast tego użyć metody:

Integer a = new Integer(128);
Integer b = new Integer(128);
System.out.println(a.equals(b));

Spowoduje to wydrukowanie: true

Możesz zapytać: Ale dlaczego wydrukowano pierwszą linię true? . Sprawdzając kod źródłowy Integer.valueOfmetody, możesz zobaczyć:

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Jeśli parametr jest liczbą całkowitą między IntegerCache.low(domyślnie -128) a IntegerCache.high(obliczona w czasie wykonywania przy minimalnej wartości 127), zwracany jest wstępnie przydzielony (buforowany) obiekt. Tak więc, gdy użyjesz 127 jako parametru, otrzymasz dwa odwołania do tego samego buforowanego obiektu i uzyskasz trueporównanie odniesień.

higuaro
źródło