Mengapa 128 == 128 salah tetapi 127 == 127 benar ketika membandingkan pembungkus Integer di Jawa?


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

Keluaran:

false

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

Keluaran:

true

Catatan: Angka antara -128 dan 127 benar.



1
bagaimana Anda sampai pada titik untuk menanyakan pertanyaan ini? itu benar-benar menyenangkan, tetapi orang tidak pernah menemukan sesuatu seperti itu "di dunia nyata" ... atau?
Mare Infinitus

Jawaban:


217

Ketika Anda mengkompilasi angka literal di Jawa dan menetapkannya ke Integer (modal I) kompiler memancarkan:

Integer b2 =Integer.valueOf(127)

Baris kode ini juga dihasilkan ketika Anda menggunakan autoboxing.

valueOf diimplementasikan sedemikian rupa sehingga angka-angka tertentu "dikumpulkan", dan mengembalikan contoh yang sama untuk nilai yang lebih kecil dari 128.

Dari kode sumber java 1.6, baris 621:

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

Nilai highdapat dikonfigurasi ke nilai lain, dengan properti sistem.

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

Jika Anda menjalankan program Anda dengan properti sistem itu, itu akan menampilkan true!

Kesimpulan yang jelas: tidak pernah bergantung pada dua referensi yang identik, selalu membandingkannya dengan .equals()metode.

Jadi b2.equals(b3)akan mencetak true untuk semua nilai yang sama secara logis dari b2, b3.

Perhatikan bahwa Integercache tidak ada untuk alasan kinerja, tetapi lebih untuk menyesuaikan dengan JLS, bagian 5.1.7 ; identitas objek harus diberikan untuk nilai -128 hingga 127 inklusif.

Integer # valueOf (int) juga mendokumentasikan perilaku ini:

metode ini cenderung menghasilkan kinerja ruang dan waktu yang lebih baik secara signifikan dengan caching nilai yang sering diminta. Metode ini akan selalu menyimpan nilai dalam rentang -128 hingga 127, inklusif, dan mungkin menyimpan nilai lainnya di luar rentang ini.


1
perhatikan bahwa nilai yang lebih kecil dari 127 akan diabaikan oleh java dan nilai yang lebih besar dari Integer.MAX_VALUE-128 akan ditutup.
Andreas Petersson

Integer di-cache untuk nilai byte di Java 5 dan lebih tinggi, membuat Integer baru (1) == Integer baru (1). Namun, ini bukan kasus di Java 1.4 atau lebih rendah, jadi berhati-hatilah jika Anda akhirnya harus turun ke lingkungan itu.
MetroidFan2002

11
tidak, ini salah. Integer baru (1) == Integer baru (1) tidak benar terlepas dari jvm. AFAIK, tidak ada kompiler yang akan menyontek pada kata kunci "baru". itu HARUS selalu instantiate objek baru.
Andreas Petersson

1
@Holger poin menarik. Tetapi secara teknis dimungkinkan untuk mengganti kelas Integer dari JDK dengan alat khusus ... (jangan tanya mengapa seseorang menjadi gila seperti itu) - maka itu bisa memiliki efek samping yang tidak diperbolehkan untuk dioptimalkan secara jauh
Andreas Petersson

1
@AndreasPetersson yakin. "Compiler" berarti kompiler JIT, yang memang tahu persis kelas implementasi aktual dan hanya dapat mengoptimalkan, jika konstruktor tidak memiliki efek samping. Atau optimalkan ekspresi untuk hanya mereproduksi efek samping, diikuti dengan penggunaan false. Sebenarnya, ini mungkin sudah terjadi hari ini, sebagai efek samping dari penerapan Analisis Escape dan Penggantian Skalar.
Holger

24

Cache autoboxing -128 hingga 127. Ini ditentukan dalam JLS ( 5.1.7 ).

Jika nilai p yang dikotakkan adalah benar, salah, satu byte, karakter dalam kisaran \ u0000 hingga \ u007f, atau int atau angka pendek antara -128 dan 127, maka biarkan r1 dan r2 menjadi hasil dari dua konversi tinju dari hal. Itu selalu terjadi bahwa r1 == r2.

Aturan sederhana untuk diingat ketika berhadapan dengan objek adalah - gunakan .equalsjika Anda ingin memeriksa apakah kedua objek "sama", gunakan ==ketika Anda ingin melihat apakah mereka menunjuk ke instance yang sama.


1
Catatan: JLS berubah di Java 9. Ini sekarang hanya dijamin untuk ekspresi konstanta waktu kompilasi ; lihat pembaruan untuk jawaban yang diterima.
Stephen C

9

Menggunakan tipe data primitif, int, akan menghasilkan true dalam kedua kasus, output yang diharapkan.

Namun, karena Anda menggunakan objek Integer, operator == memiliki arti yang berbeda.

Dalam konteks objek, == memeriksa untuk melihat apakah variabel merujuk ke referensi objek yang sama.

Untuk membandingkan nilai objek, Anda harus menggunakan metode equals () Eg

 b2.equals(b1)

yang akan menunjukkan apakah b2 kurang dari b1, lebih besar dari, atau sama dengan (periksa API untuk detail)


7

Ini adalah optimasi memori terkait Java.

Untuk menghemat memori, Java 'menggunakan kembali' semua objek pembungkus yang nilainya jatuh dalam rentang berikut:

Semua nilai Boolean (benar dan salah)

Semua nilai Byte

Semua nilai karakter dari \ u0000 hingga \ u007f (mis. 0 hingga 127 dalam desimal)

Semua nilai Short dan Integer dari -128 hingga 127.


3

Lihatlah Integer.java, jika nilainya antara -128 dan 127, ia akan menggunakan cached pool, jadi (Integer) 1 == (Integer) 1sementara(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

Jawaban lain menjelaskan mengapa efek yang diamati dapat diamati, tetapi itu benar-benar tidak penting bagi pemrogram (menarik, tentu saja, tetapi sesuatu yang harus Anda lupakan saat menulis kode yang sebenarnya).

Untuk membandingkan objek Integer untuk kesetaraan, gunakan equalsmetode ini.

Jangan mencoba membandingkan objek Integer untuk persamaan dengan menggunakan operator identitas ==,.

Mungkin terjadi bahwa beberapa nilai yang sama adalah objek yang identik, tetapi ini bukan sesuatu yang secara umum harus diandalkan.


-4

Saya menulis yang berikut karena masalah ini tidak hanya spesifik untuk Integer. Kesimpulan saya adalah bahwa lebih sering daripada tidak jika Anda menggunakan API secara tidak benar, Anda masih akan melihat perilaku yang salah. Gunakan dengan benar dan Anda akan melihat perilaku yang benar:

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

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.