Perché == i confronti con Integer.valueOf (String) danno risultati diversi per 127 e 128?


182

Non ho idea del perché queste righe di codice restituiscano valori diversi:

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

L'output è:

true
false
true

Perché il primo ritorna truee il secondo ritorna false? C'è qualcosa di diverso che non conosco tra 127e 128? (Certo che so che 127< 128.)

Inoltre, perché il terzo ritorna true?

Ho letto la risposta a questa domanda , ma non ho ancora capito come può tornare truee perché il codice in seconda riga ritorni false.


6
Integer è un oggetto; se vuoi confrontare per uguaglianza, usa .equals(), altrimenti tutte le scommesse sono disattivate.
Karl Damgaard Asmussen,

6
@KarlDamgaardAsmussen In realtà qui voglio davvero verificare se sono riferimenti allo stesso oggetto, e all'inizio non capisco perché 127 128 restituiscono risultati diversi.
DnR

@DnR se Java fosse un linguaggio con una specifica standardizzata, penso che avrebbe permesso a tali questioni di implementarsi o addirittura di imporre comportamenti indefiniti.
Karl Damgaard Asmussen,

1
@jszumski: c'è di più in questa domanda oltre alla sola cache. Inoltre, la risposta collegata è nella migliore delle ipotesi incompleta - non è del tutto dettagliata nei dettagli di cosa è memorizzato nella cache e perché.
Makoto,

1
Per ulteriore seguito su questa discussione, si prega di fare riferimento a questo meta post .
Jeroen Vannevel,

Risposte:


191

C'è una differenza notevole qui.

valueOfsta restituendo un Integeroggetto, che può avere i suoi valori memorizzati nella cache tra -128 e 127. Ecco perché il primo valore restituisce true- è memorizzato nella cache - e il secondo valore restituito false- 128 non è un valore memorizzato nella cache, quindi si ottengono due Integeristanze separate .

E 'importante notare che si sta confrontando con i riferimenti Integer#valueOf, e se si sta confrontando un valore che è più grande di quello dei supporti di cache, esso non è valutata come true, anche se i valori analizzati sono equivalenti (caso emblematico: Integer.valueOf(128) == Integer.valueOf(128)). È necessario utilizzare equals()invece.

parseIntsta tornando un primitivo int. Questo è il motivo per cui il terzo valore rendimenti true- 128 == 128viene valutata, ed è naturalmente true.

Ora, un bel po 'capita di ottenere quel terzo risultato true:

  • Si verifica una conversione di unboxing rispetto all'operatore di equivalenza che si sta utilizzando e ai tipi di dati che si hanno - vale a dire, inte Integer. Stai ricevendo un Integerdalla valueOfparte di destra, ovviamente.

  • Dopo la conversione, stai confrontando due intvalori primitivi . Il confronto avviene esattamente come ci si aspetterebbe rispetto ai primitivi, quindi si finisce per confrontare 128e 128.


2
@ user3152527: c'è una differenza considerevole: uno è considerato un oggetto, il che significa che puoi chiamare metodi e interagire con esso in strutture di dati astratte, come List. L'altro è un primitivo, che è solo un valore grezzo.
Makoto,

1
@ user3152527 Hai fatto una domanda eccellente (e non una stupida nel peggiore dei casi). Ma l'hai corretto per usare .equals, giusto?
user2910265

3
Ah, sembra che l'interrogatore non abbia capito un fatto di fondo in Java: quando si utilizza "==" per confrontare due oggetti, si sta verificando se si tratta di riferimenti allo stesso oggetto. Quando si utilizza "equals ()", si sta verificando se hanno lo stesso valore. Non è possibile utilizzare "uguale" per confrontare le primitive.
Jay,

3
@Jay no, lo capisco. ma quello che inizialmente mi confonde è il motivo per cui il primo restituisce vero e il secondo restituisce falso usando lo stesso metodo di confronto ==. comunque, adesso è chiaro.
DnR

1
Nit: non è solo che Integer "può" essere memorizzato nella cache tra -128 e 127. Deve essere, secondo JLS 5.1.7 . Esso può essere memorizzata nella cache al di fuori di tale intervallo, ma non deve essere (e spesso non lo è).
yshavit,

127

La Integerclasse ha una cache statica, che memorizza 256 Integeroggetti speciali , uno per ogni valore compreso tra -128 e 127. Tenendo presente ciò, considera la differenza tra questi tre.

new Integer(123);

Questo (ovviamente) rende un Integeroggetto nuovo di zecca .

Integer.parseInt("123");

Questo restituisce un intvalore primitivo dopo aver analizzato il String.

Integer.valueOf("123");

Questo è più complesso degli altri. Si inizia analizzando il String. Quindi, se il valore è compreso tra -128 e 127, restituisce l'oggetto corrispondente dalla cache statica. Se il valore è al di fuori di questo intervallo, invoca new Integer()e passa il valore, in modo da ottenere un nuovo oggetto.

Ora, considera le tre espressioni nella domanda.

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

Ciò restituisce true, poiché il Integercui valore è 127 viene recuperato due volte dalla cache statica e confrontato con se stesso. C'è solo un Integeroggetto coinvolto, quindi questo ritorna true.

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

Ciò restituisce false, poiché 128 non è nella cache statica. Quindi Integerviene creato un nuovo per ogni lato dell'uguaglianza. Dal momento che ci sono due Integeroggetti diversi , e ==per gli oggetti ritorna solo truese entrambi i lati sono esattamente lo stesso oggetto, lo sarà false.

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

Questo sta confrontando il intvalore primitivo 128 a sinistra, con un Integeroggetto appena creato a destra. Ma poiché non ha senso confrontare an intcon an Integer, Java si auto-decomprimerà Integerprima di fare il confronto; quindi si finisce per confrontare an intcon an int. Poiché la primitiva 128 è uguale a se stessa, questo ritorna true.


13

Abbi cura di restituire valori da questi metodi. Il metodo valueOf restituisce un'istanza Integer:

public static Integer valueOf(int i)

Il metodo parseInt restituisce un valore intero (tipo primitivo):

public static int parseInt(String s) throws NumberFormatException

Spiegazione per il confronto:

Per risparmiare memoria, due istanze degli oggetti wrapper saranno sempre == quando i loro valori primitivi sono gli stessi:

  • booleano
  • Byte
  • Carattere da \ u0000 a \ u007f (7f è 127 in decimale)
  • Corto e intero da -128 a 127

Quando == viene utilizzato per confrontare una primitiva con un wrapper, il wrapper verrà scartato e il confronto sarà primitivo con la primitiva.

Nella tua situazione (secondo le regole di cui sopra):

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

Questa espressione confronta i riferimenti allo stesso oggetto perché contiene un valore intero compreso tra -128 e 127, quindi restituisce true.

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

Questa espressione confronta i riferimenti a oggetti diversi perché contengono valori Integer non in <-128, 127>, quindi restituisce false.

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

Questa espressione confronta il valore primitivo (lato sinistro) e il riferimento all'oggetto (lato destro), quindi il lato destro verrà scartato e il suo tipo primitivo verrà confrontato con il lato sinistro in modo che ritorni true.



Potete fornire un URL per l'origine dell'offerta?
Philzen,

"... due istanze degli oggetti wrapper, saranno sempre == quando i loro valori primitivi sono gli stessi ..." - assolutamente falso. Se si creano due oggetti wrapper con lo stesso valore, questi non restituiranno true se confrontati con ==, poiché sono oggetti diversi.
Dawood ibn Kareem il

6

Gli oggetti interi vengono memorizzati nella cache tra -128 e 127 di 256 numeri interi

Non confrontare i riferimenti agli oggetti con == o ! = . Dovresti usare . equals (..) invece, o meglio - usa int primitivo anziché Integer.

parseInt : analizza l'argomento stringa come un intero decimale con segno. I caratteri nella stringa devono essere tutti cifre decimali, tranne per il fatto che il primo carattere può essere un segno meno ASCII '-' ('\ u002D') per indicare un valore negativo. Viene restituito il valore intero risultante, esattamente come se l'argomento e la radice 10 fossero dati come argomenti al metodo parseInt (java.lang.String, int).

valueOf Restituisce un oggetto Integer che contiene il valore estratto dalla stringa specificata quando analizzato con la radice fornita dal secondo argomento. Il primo argomento viene interpretato come rappresentante di un intero con segno nella radice specificata dal secondo argomento, esattamente come se gli argomenti fossero stati dati al metodo parseInt (java.lang.String, int). Il risultato è un oggetto intero che rappresenta il valore intero specificato dalla stringa.

equivalente a

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

radix - la radix da utilizzare nell'interpretazione di s

quindi se si eguaglia Integer.valueOf()per l'intero tra

Da -128 a 127 ritorna vero nelle tue condizioni

per lesser than-128 e greater than127 dàfalse


6

Per integrare le risposte fornite, prendere nota anche di quanto segue:

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

Questo codice stampa anche: false

Come ha affermato l' utente Jay in un commento per la risposta accettata, è necessario prestare attenzione quando si utilizza l'operatore ==sugli oggetti, qui si sta verificando se entrambi i riferimenti sono uguali, il che non lo è, poiché sono oggetti diversi, sebbene rappresentino il vero stesso valore. Per confrontare gli oggetti, è necessario utilizzare equals invece il metodo:

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

Questo stamperà: true

Potresti chiedere, ma allora perché la prima riga è stata stampata true? . Controllando il codice sorgente per il Integer.valueOfmetodo, puoi vedere quanto segue:

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

Se il parametro è un numero intero compreso tra IntegerCache.low(predefinito a -128) e IntegerCache.high(calcolato in fase di esecuzione con valore minimo 127), viene restituito un oggetto pre-allocato (memorizzato nella cache). Pertanto, quando si utilizza 127 come parametro, si ottengono due riferimenti allo stesso oggetto memorizzato nella cache e si ottiene trueil confronto dei riferimenti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.