Warum ergeben == Vergleiche mit Integer.valueOf (String) unterschiedliche Ergebnisse für 127 und 128?

182

Ich habe keine Ahnung, warum diese Codezeilen unterschiedliche Werte zurückgeben:

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"));

Die Ausgabe ist:

true
false
true

Warum kehrt der erste trueund der zweite zurück false? Gibt es etwas anderes, das ich zwischen 127und nicht weiß 128? (Natürlich weiß ich das 127< 128.)

Warum kehrt der dritte zurück true?

Ich habe die Antwort auf diese Frage gelesen , aber ich habe immer noch nicht verstanden, wie sie zurückgegeben werden kann trueund warum der Code in der zweiten Zeile zurückgegeben wird false.

DnR
quelle
6
Ganzzahl ist ein Objekt; Wenn Sie auf Gleichheit vergleichen möchten, verwenden Sie .equals(), andernfalls sind alle Wetten ungültig.
Karl Damgaard Asmussen
6
@KarlDamgaardAsmussen Eigentlich möchte ich hier wirklich testen, ob es sich um Verweise auf dasselbe Objekt handelt, und zunächst verstehe ich nicht, warum 127 128 ein anderes Ergebnis zurückgeben.
DnR
@DnR Wenn Java eine Sprache mit einer standardisierten Spezifikation wäre, würde ich denken, dass solche Angelegenheiten bis zur Implementierung oder sogar undefiniertem Verhalten vorgeschrieben sind.
Karl Damgaard Asmussen
1
@jszumski: Diese Frage beinhaltet jedoch mehr als nur den Caching-Teil. Außerdem ist die verknüpfte Antwort bestenfalls unvollständig - sie geht nicht ganz ins Detail, was zwischengespeichert wird und warum.
Makoto
1
Weitere Informationen zu dieser Diskussion finden Sie in diesem Meta-Beitrag .
Jeroen Vannevel

Antworten:

191

Hier gibt es einen bemerkenswerten Unterschied.

valueOfgibt ein IntegerObjekt zurück, dessen Werte möglicherweise zwischen -128 und 127 zwischengespeichert sind. Aus diesem Grund gibt der erste Wert zurück true- er wird zwischengespeichert - und der zweite Wert gibt zurück false- 128 ist kein zwischengespeicherter Wert, sodass Sie zwei separate IntegerInstanzen erhalten .

Es ist wichtig zu beachten, dass Sie Referenzen mit vergleichen Integer#valueOfund wenn Sie einen Wert vergleichen, der größer ist als der vom Cache unterstützte Wert, wird er nicht ausgewertet true, selbst wenn die analysierten Werte äquivalent sind (Beispiel :) Integer.valueOf(128) == Integer.valueOf(128). Sie müssen verwenden equals()statt.

parseIntgibt ein Primitiv zurück int. Aus diesem Grund gibt der dritte Wert zurück true- 128 == 128wird ausgewertet und ist es natürlich true.

Nun passiert ein gutes Stück, um das dritte Ergebnis zu erzielen true:

  • Eine Unboxing-Konvertierung erfolgt in Bezug auf den von Ihnen verwendeten Äquivalenzoperator und die von Ihnen verwendeten Datentypen - nämlich intund Integer. Du bekommst natürlich eine Integervon valueOfauf der rechten Seite.

  • Nach der Konvertierung vergleichen Sie zwei primitive intWerte. Vergleich geschieht nur , wie Sie es mit Bezug auf Primitiven erwarten würden, so dass Sie aufzuwickeln vergleichen 128und 128.

Makoto
quelle
2
@ user3152527: Es gibt einen beträchtlichen Unterschied - eines wird als Objekt betrachtet, dh Sie können Methoden aufrufen und in abstrakten Datenstrukturen wie z List. Das andere ist ein Grundelement, das nur ein Rohwert ist.
Makoto
1
@ user3152527 Du hast eine ausgezeichnete Frage gestellt (und im schlimmsten Fall keine dumme). Aber Sie haben es behoben, um .equals zu verwenden, richtig?
user2910265
3
Ah, es scheint, dass der Fragesteller eine zugrunde liegende Tatsache in Java nicht verstanden hat: Wenn Sie "==" zum Vergleichen von zwei Objekten verwenden, testen Sie, ob es sich um Verweise auf dasselbe Objekt handelt. Wenn Sie "equals ()" verwenden, testen Sie, ob sie denselben Wert haben. Sie können nicht "gleich" verwenden, um Grundelemente zu vergleichen.
Jay
3
@ Jay nein, das verstehe ich. Aber das, was mich zuerst verwirrt, ist, warum das erste mit derselben Vergleichsmethode true und das zweite false zurückgibt ==. Jedenfalls ist es jetzt klar.
DnR
1
Nit: Es ist nicht nur so, dass Integer zwischen -128 und 127 zwischengespeichert werden kann . Dies muss laut JLS 5.1.7 der Fall sein . Es kann außerhalb dieses Bereichs zwischengespeichert werden, muss es aber nicht sein (und ist es oft nicht).
Yshavit
127

Die IntegerKlasse verfügt über einen statischen Cache, in dem 256 spezielle IntegerObjekte gespeichert sind - eines für jeden Wert zwischen -128 und 127. Berücksichtigen Sie in diesem Zusammenhang den Unterschied zwischen diesen drei.

new Integer(123);

Dies macht (offensichtlich) ein brandneues IntegerObjekt.

Integer.parseInt("123");

Dies gibt intnach dem Parsen des einen primitiven Wert zurück String.

Integer.valueOf("123");

Das ist komplexer als die anderen. Es beginnt mit dem Parsen des String. Wenn der Wert dann zwischen -128 und 127 liegt, wird das entsprechende Objekt aus dem statischen Cache zurückgegeben. Wenn der Wert außerhalb dieses Bereichs liegt, wird new Integer()der Wert aufgerufen und übergeben, sodass Sie ein neues Objekt erhalten.

Betrachten Sie nun die drei Ausdrücke in der Frage.

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

Dies gibt true zurück, da der IntegerWert 127 zweimal aus dem statischen Cache abgerufen und mit sich selbst verglichen wird. Es ist nur ein IntegerObjekt beteiligt, daher kehrt dieses zurück true.

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

Dies wird zurückgegeben false, da sich 128 nicht im statischen Cache befindet. So Integerwird für jede Seite der Gleichheit eine neue geschaffen. Da es zwei verschiedene IntegerObjekte gibt und ==für Objekte nur zurückgegeben wird, truewenn beide Seiten genau dasselbe Objekt sind, wird dies der Fall sein false.

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

Dies vergleicht den primitiven intWert 128 links mit einem neu erstellten IntegerObjekt rechts. Da es jedoch nicht sinnvoll ist, ein intmit einem zu vergleichen Integer, wird Java das Integervor dem Vergleich automatisch entpacken . Sie vergleichen also am Ende ein intmit einem int. Da das Grundelement 128 gleich sich selbst ist, kehrt dies zurück true.

Dawood ibn Kareem
quelle
13

Achten Sie darauf, Werte von diesen Methoden zurückzugeben. Die valueOf- Methode gibt eine Integer-Instanz zurück:

public static Integer valueOf(int i)

Die parseInt- Methode gibt einen ganzzahligen Wert (primitiver Typ) zurück:

public static int parseInt(String s) throws NumberFormatException

Erklärung zum Vergleich:

Um Speicherplatz zu sparen, sind zwei Instanzen der Wrapper-Objekte immer ==, wenn ihre primitiven Werte gleich sind:

  • Boolescher Wert
  • Byte
  • Zeichen von \ u0000 bis \ u007f (7f ist 127 in Dezimalzahl)
  • Short und Integer von -128 bis 127

Wenn == verwendet wird, um ein Grundelement mit einem Wrapper zu vergleichen, wird der Wrapper entpackt und der Vergleich ist primitiv mit dem Grundelement.

In Ihrer Situation (gemäß den oben genannten Regeln):

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

Dieser Ausdruck vergleicht Verweise auf dasselbe Objekt, da er einen Integer-Wert zwischen -128 und 127 enthält, sodass er zurückgegeben wird true.

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

Dieser Ausdruck vergleicht Verweise auf verschiedene Objekte, da sie Integer-Werte enthalten, die nicht in <-128, 127> enthalten sind, und gibt daher zurück false.

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

Dieser Ausdruck vergleicht den primitiven Wert (linke Seite) und den Verweis auf das Objekt (rechte Seite), sodass die rechte Seite entpackt wird und sein primitiver Typ mit dem linken verglichen wird, damit er zurückkehrt true.

Piobab
quelle
3
Ähnliche Frage: stackoverflow.com/questions/9824053/…
Piobab
Können Sie eine URL für die Angebotsquelle angeben?
Philzen
"... zwei Instanzen der Wrapper-Objekte sind immer ==, wenn ihre primitiven Werte gleich sind ..." - absolut falsch. Wenn Sie zwei Wrapper-Objekte mit demselben Wert erstellen, geben sie im Vergleich zu nicht true zurück ==, da es sich um unterschiedliche Objekte handelt.
Dawood ibn Kareem
6

Integer-Objekte werden zwischen -128 und 127 von 256 Integer zwischengespeichert

Sie sollten Objektreferenzen nicht mit == oder ! = Vergleichen . Du solltest benutzen . gleich (..) oder besser - verwenden Sie das primitive int anstelle von Integer.

parseInt : Analysiert das Zeichenfolgenargument als vorzeichenbehaftete Dezimalzahl. Die Zeichen in der Zeichenfolge müssen alle Dezimalstellen sein, mit der Ausnahme, dass das erste Zeichen ein ASCII-Minuszeichen '-' ('\ u002D') sein kann, um einen negativen Wert anzuzeigen. Der resultierende ganzzahlige Wert wird genau so zurückgegeben, als ob das Argument und der Radix 10 als Argumente für die parseInt-Methode (java.lang.String, int) angegeben wurden.

valueOf Gibt ein Integer-Objekt zurück, das den aus dem angegebenen String extrahierten Wert enthält, wenn es mit dem durch das zweite Argument angegebenen Radix analysiert wird. Das erste Argument wird so interpretiert, dass es eine vorzeichenbehaftete Ganzzahl in dem durch das zweite Argument angegebenen Radix darstellt, genau so, als ob die Argumente der parseInt-Methode (java.lang.String, int) übergeben würden. Das Ergebnis ist ein Integer-Objekt, das den durch die Zeichenfolge angegebenen Integer-Wert darstellt.

gleichwertig

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

radix - der Radix, der bei der Interpretation von s verwendet werden soll

Also, wenn Sie Integer.valueOf()für die ganze Zahl dazwischen gleich sind

-128 bis 127 gibt es in Ihrem Zustand true zurück

für lesser than-128 und greater than127 gibt esfalse

Nambi
quelle
6

Beachten Sie zur Ergänzung der gegebenen Antworten auch Folgendes:

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

Dieser Code wird auch gedruckt: false

Wie Benutzer Jay in einem Kommentar für die akzeptierte Antwort behauptet hat, muss bei der Verwendung des Operators ==für Objekte vorsichtig vorgegangen werden. Hier überprüfen Sie, ob beide Referenzen gleich sind, was nicht der Fall ist, da es sich um unterschiedliche Objekte handelt, obwohl sie genau das darstellen gleicher Wert. Um Objekte zu vergleichen, sollten Sie equals stattdessen die folgende Methode verwenden:

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

Dies wird gedruckt: true

Sie fragen sich vielleicht, aber warum wurde dann die erste Zeile gedruckt true? . Wenn Sie den Quellcode für die Integer.valueOfMethode überprüfen, sehen Sie Folgendes:

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);
}

Wenn der Parameter eine Ganzzahl zwischen IntegerCache.low(standardmäßig -128) und IntegerCache.high(zur Laufzeit mit dem Mindestwert 127 berechnet) ist, wird ein vorab zugewiesenes (zwischengespeichertes) Objekt zurückgegeben. Wenn Sie also 127 als Parameter verwenden, erhalten Sie zwei Referenzen auf dasselbe zwischengespeicherte Objekt und erhalten einen trueVergleich der Referenzen.

Higuaro
quelle