Perché 128 == 128 è falso ma 127 == 127 è vero quando si confrontano i wrapper Integer in Java?


172
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

Produzione:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

Produzione:

true

Nota: i numeri tra -128 e 127 sono veri.



1
come sei arrivato al punto di porre questa domanda? è davvero divertente, ma non ci si imbatte mai in qualcosa del genere "nel mondo reale" ... o?
Mare Infinitus,

Risposte:


217

Quando compili un numero letterale in Java e lo assegni a un numero intero (maiuscolo I) il compilatore emette:

Integer b2 =Integer.valueOf(127)

Questa riga di codice viene generata anche quando si utilizza il box automatico.

valueOf è implementato in modo tale che alcuni numeri siano "raggruppati" e restituisce la stessa istanza per valori inferiori a 128.

Dal codice sorgente java 1.6, riga 621:

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

Il valore di highpuò essere configurato su un altro valore, con la proprietà di sistema.

-Djava.lang.Integer.IntegerCache.high = 999

Se esegui il tuo programma con quella proprietà di sistema, verrà generato vero!

La conclusione ovvia: non fare mai affidamento su due riferimenti identici, confrontarli sempre con il .equals()metodo.

Quindi b2.equals(b3)verrà stampato true per tutti i valori logicamente uguali di b2, b3.

Si noti che la Integercache non è presente per motivi di prestazioni, ma piuttosto per conformarsi a JLS, sezione 5.1.7 ; l'identità dell'oggetto deve essere fornita per valori compresi tra -128 e 127 inclusi.

Integer # valueOf (int) documenta anche questo comportamento:

è probabile che questo metodo produca prestazioni nello spazio e nel tempo significativamente migliori memorizzando nella cache i valori richiesti di frequente. Questo metodo memorizzerà sempre nella cache valori compresi tra -128 e 127, inclusi, e potrebbe memorizzare nella cache altri valori al di fuori di questo intervallo.


1
si noti che i valori inferiori a 127 verranno ignorati da java e i valori maggiori di Integer.MAX_VALUE-128 saranno limitati.
Andreas Petersson,

I numeri interi vengono memorizzati nella cache per i valori di byte in Java 5 e versioni successive, creando un nuovo numero intero (1) == un nuovo numero intero (1). Tuttavia, questo non è il caso di Java 1.4 o versioni precedenti, quindi fai attenzione se alla fine devi eseguire il downgrade a quell'ambiente.
MetroidFan2002,

11
no, questo è sbagliato. new Integer (1) == new Integer (1) è falso indipendentemente da jvm. AFAIK nessun compilatore tradirà la "nuova" parola chiave. DEVE sempre creare un'istanza di un nuovo oggetto.
Andreas Petersson,

1
@Holger punto interessante. Ma è tecnicamente possibile sostituire la classe Integer dal JDK con un impl personalizzato ... (non chiedere perché qualcuno sarebbe così pazzo) - allora potrebbe avere effetti collaterali che non possono ottimizzare via
Andreas Petersson

1
@AndreasPetersson certo. "Compilatore" indica il compilatore JIT, che conosce con precisione l'attuale classe di implementazione e può ottimizzare solo se il costruttore non ha effetti collaterali. Oppure ottimizza l'espressione per riprodurre solo gli effetti collaterali, seguito dall'uso false. In realtà, questo può già accadere oggi, come effetto collaterale dell'applicazione dell'analisi di fuga e della sostituzione scalare.
Holger,

24

Cache di box automatico da -128 a 127. Questo è specificato in JLS ( 5.1.7 ).

Se il valore p in box è vero, falso, un byte, un carattere nell'intervallo da \ u0000 a \ u007f o un numero int o short tra -128 e 127, quindi lasciare che r1 e r2 siano i risultati di due conversioni di boxe di p. È sempre il caso che r1 == r2.

Una semplice regola da ricordare quando si ha a che fare con gli oggetti è - usare .equalsse si desidera verificare se i due oggetti sono "uguali", usare ==quando si vuole vedere se puntano alla stessa istanza.


1
Nota: JLS è cambiato in Java 9. Questo è ora garantito solo per le espressioni delle costanti di tempo di compilazione ; vedi aggiornamento alla risposta accettata.
Stephen C,

9

L'uso di tipi di dati primitivi, ints, produrrebbe vero in entrambi i casi, l'output previsto.

Tuttavia, poiché stai utilizzando oggetti Integer, l'operatore == ha un significato diverso.

Nel contesto degli oggetti, == controlla se le variabili si riferiscono allo stesso riferimento di oggetto.

Per confrontare il valore degli oggetti è necessario utilizzare il metodo equals () Ad es

 b2.equals(b1)

che indicherà se b2 è minore di b1, maggiore o uguale a (controllare l'API per i dettagli)


7

È l'ottimizzazione della memoria in Java.

Per risparmiare memoria, Java "riutilizza" tutti gli oggetti wrapper i cui valori rientrano nei seguenti intervalli:

Tutti i valori booleani (vero e falso)

Tutti i valori byte

Tutti i valori dei caratteri da \ u0000 a \ u007f (ovvero da 0 a 127 in decimale)

Tutti i valori Short e Integer da -128 a 127.


3

Dai un'occhiata a Integer.java, se il valore è compreso tra -128 e 127, utilizzerà il pool memorizzato nella cache, quindi (Integer) 1 == (Integer) 1mentre(Integer) 222 != (Integer) 222

 /**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #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.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
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);
}       

0

Altre risposte descrivono il motivo per cui gli effetti osservati possono essere osservati, ma questo è davvero irrilevante per i programmatori (interessante, certamente, ma qualcosa di cui dovresti dimenticare tutto quando scrivi il codice reale).

Per confrontare oggetti interi per uguaglianza, utilizzare il equalsmetodo

Non tentare di confrontare gli oggetti Integer per l'uguaglianza utilizzando l'operatore identità, ==.

Può accadere che alcuni valori uguali siano oggetti identici, ma questo non è qualcosa su cui generalmente si dovrebbe fare affidamento.


-4

Ho scritto quanto segue poiché questo problema non è solo specifico di Integer. La mia conclusione è che il più delle volte se si utilizza l'API in modo errato, si vedrà un comportamento errato. Usalo correttamente e dovresti vedere il comportamento corretto:

public static void main (String[] args) {
    Byte b1=127;
    Byte b2=127;

    Short s1=127; //incorrect should use Byte
    Short s2=127; //incorrect should use Byte
    Short s3=128;
    Short s4=128;

    Integer i1=127; //incorrect should use Byte
    Integer i2=127; //incorrect should use Byte
    Integer i3=128;
    Integer i4=128;

    Integer i5=32767; //incorrect should use Short
    Integer i6=32767; //incorrect should use Short

    Long l1=127L;           //incorrect should use Byte
    Long l2=127L;           //incorrect should use Byte
    Long l3=13267L;         //incorrect should use Short
    Long l4=32767L;         //incorrect should use Short
    Long l5=2147483647L;    //incorrect should use Integer 
    Long l6=2147483647L;    //incorrect should use Integer
    Long l7=2147483648L;
    Long l8=2147483648L;

    System.out.print(b1==b2); //true  (incorrect) Used API correctly
    System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
    System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
    System.out.print(l1==l2); //true  (incorrect) Used API incorrectly

    System.out.print(s3==s4); //false (correct) Used API correctly
    System.out.print(i3==i4); //false (correct) Used API correctly
    System.out.print(i5==i6); //false (correct) Used API correctly
    System.out.print(l3==l4); //false (correct) Used API correctly
    System.out.print(l7==l8); //false (correct) Used API correctly
    System.out.print(l5==l6); //false (correct) Used API incorrectly

}
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.