Mengapa perbandingan == dengan Integer.valueOf (String) memberikan hasil yang berbeda untuk 127 dan 128?

182

Saya tidak tahu mengapa baris kode ini mengembalikan nilai yang berbeda:

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

Outputnya adalah:

true
false
true

Mengapa yang pertama kembali truedan yang kedua kembali false? Apakah ada sesuatu yang berbeda yang tidak saya ketahui antara 127dan 128? (Tentu saja aku tahu itu 127< 128.)

Juga, mengapa yang ketiga kembali true?

Saya telah membaca jawaban dari pertanyaan ini , tetapi saya masih belum mengerti bagaimana ia dapat kembali true, dan mengapa kode di baris kedua kembali false.

DnR
sumber
6
Integer adalah objek; jika Anda ingin membandingkan untuk kesetaraan, gunakan .equals(), jika tidak semua taruhan dibatalkan.
Karl Damgaard Asmussen
6
@KarlDamgaardAsmussen Sebenarnya di sini saya benar-benar ingin menguji apakah mereka referensi ke objek yang sama, dan pada awalnya saya tidak mengerti mengapa 127 128 mengembalikan hasil yang berbeda.
DnR
@ DNR jika Java adalah bahasa dengan spesifikasi standar, saya akan berpikir itu membiarkan hal-hal seperti hingga implementasi atau bahkan mandat perilaku yang tidak ditentukan.
Karl Damgaard Asmussen
1
@ jszumski: Namun, ada banyak hal untuk pertanyaan ini selain dari bagian caching. Selain itu, jawaban yang terhubung tidak lengkap di terbaik - tidak cukup detail tentang apa yang di-cache dan mengapa.
Makoto
1
Untuk tindak lanjut lebih lanjut pada diskusi ini, silakan merujuk ke pos meta ini .
Jeroen Vannevel

Jawaban:

191

Ada perbedaan mencolok di sini.

valueOfmengembalikan Integerobjek, yang mungkin memiliki nilai-nilai di-cache antara -128 dan 127. Inilah sebabnya mengapa nilai pertama kembali true- itu di-cache - dan nilai kedua kembali false- 128 bukan nilai di-cache, sehingga Anda mendapatkan dua Integercontoh terpisah .

Penting untuk dicatat bahwa Anda membandingkan referensi dengan Integer#valueOf, dan jika Anda membandingkan nilai yang lebih besar dari yang didukung cache, itu tidak akan dievaluasi true, bahkan jika nilai yang diuraikan adalah setara (dalam kasus:) Integer.valueOf(128) == Integer.valueOf(128). Anda harus menggunakannya equals()sebagai gantinya.

parseIntmengembalikan primitif int. Inilah sebabnya mengapa pengembalian nilai ketiga true- 128 == 128dievaluasi, dan tentu saja true,.

Sekarang, sedikit wajar terjadi untuk membuat hasil ketiga true:

  • Konversi unboxing terjadi sehubungan dengan operator ekivalen yang Anda gunakan dan tipe data yang Anda miliki - yaitu, intdan Integer. Anda mendapatkan Integerdari valueOfdi sisi kanan, tentu saja.

  • Setelah konversi, Anda membandingkan dua intnilai primitif . Perbandingan terjadi persis seperti yang Anda harapkan sehubungan dengan primitif, jadi Anda akhirnya membandingkan 128dan 128.

Makoto
sumber
2
@ user3152527: Ada perbedaan yang cukup besar - satu dianggap objek, yang berarti Anda dapat memanggil metode dan berinteraksi dengannya dalam struktur data abstrak, seperti List. Yang lainnya adalah primitif, yang hanya nilai mentah.
Makoto
1
@ user3152527 Anda mengajukan pertanyaan yang sangat baik (dan bukan yang bodoh paling buruk). Tapi Anda sudah memperbaikinya untuk menggunakan. Sama, kan?
user2910265
3
Ah, tampaknya si penanya tidak memahami fakta mendasar di Jawa: Saat menggunakan "==" untuk membandingkan dua objek, Anda menguji apakah mereka merujuk ke objek yang sama. Saat menggunakan "equals ()", Anda menguji apakah mereka memiliki nilai yang sama. Anda tidak dapat menggunakan "sama dengan" untuk membandingkan primitif.
Jay
3
@Jay tidak, saya mengerti itu. tetapi yang membingungkan saya pada awalnya adalah mengapa yang pertama mengembalikan true dan yang kedua return false menggunakan metode perbandingan yang sama ==. Lagi pula, sudah jelas sekarang.
DNR
1
Nit: bukan saja Integer "boleh" di-cache antara -128 dan 127. Harus , menurut JLS 5.1.7 . Itu mungkin di -cache di luar rentang itu, tetapi tidak harus (dan sering tidak).
yshavit
127

The Integerkelas memiliki cache statis, yang menyimpan 256 khusus Integerbenda - satu untuk setiap nilai antara -128 dan 127. Dengan itu dalam pikiran, mempertimbangkan perbedaan antara ketiganya.

new Integer(123);

Ini (jelas) membuat Integerobjek baru .

Integer.parseInt("123");

Ini mengembalikan intnilai primitif setelah parsing the String.

Integer.valueOf("123");

Ini lebih kompleks daripada yang lain. Ini dimulai dengan parsing the String. Kemudian, jika nilainya antara -128 dan 127, itu mengembalikan objek yang sesuai dari cache statis. Jika nilainya di luar kisaran ini, maka new Integer()nilainya akan memanggil dan meneruskan nilainya, sehingga Anda mendapatkan objek baru.

Sekarang, perhatikan tiga ungkapan dalam pertanyaan.

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

Ini mengembalikan true, karena Integeryang nilainya 127 diambil dua kali dari cache statis, dan dibandingkan dengan itu sendiri. Hanya ada satu Integerobjek yang terlibat, jadi ini kembali true.

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

Ini kembali false, karena 128 tidak ada dalam cache statis. Jadi yang baru Integerdibuat untuk setiap sisi kesetaraan. Karena ada dua Integerobjek yang berbeda , dan ==untuk objek hanya kembali truejika kedua sisi adalah objek yang sama persis, ini akan menjadi false.

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

Ini membandingkan nilai primitif int128 di sebelah kiri, dengan objek yang baru dibuat Integerdi sebelah kanan. Tetapi karena tidak masuk akal untuk membandingkan intdengan Integer, Java akan membuka kotak secara otomatis Integersebelum melakukan perbandingan; sehingga Anda akhirnya membandingkan intdengan int. Karena primitif 128 sama dengan dirinya sendiri, ini kembali true.

Dawood ibn Kareem
sumber
13

Jaga pengembalian nilai dari metode ini. The valueOf metode mengembalikan sebuah contoh Integer:

public static Integer valueOf(int i)

The parseInt kembali metode bilangan bulat nilai (tipe primitif):

public static int parseInt(String s) throws NumberFormatException

Penjelasan untuk perbandingan:

Untuk menghemat memori, dua contoh objek wrapper, akan selalu == ketika nilai primitifnya sama:

  • Boolean
  • Byte
  • Karakter dari \ u0000 hingga \ u007f (7f adalah 127 dalam desimal)
  • Pendek dan Integer dari -128 hingga 127

Ketika == digunakan untuk membandingkan primitif ke pembungkus, pembungkus akan terbuka dan perbandingan akan primitif ke primitif.

Dalam situasi Anda (sesuai dengan aturan di atas):

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

Ekspresi ini membandingkan referensi ke objek yang sama karena mengandung nilai Integer antara -128 dan 127 sehingga kembali true.

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

Ungkapan ini membandingkan referensi ke objek yang berbeda karena mengandung nilai Integer yang tidak dalam <-128, 127> sehingga mengembalikan false.

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

Ungkapan ini membandingkan nilai primitif (sisi kiri) dan referensi ke objek (sisi kanan) sehingga sisi kanan akan dibuka dan tipe primitifnya akan dibandingkan dengan kiri sehingga kembali true.

piobab
sumber
3
Pertanyaan serupa: stackoverflow.com/questions/9824053/…
piobab
Bisakah Anda memberikan URL untuk sumber kutipan?
Philzen
"... dua contoh objek pembungkus, akan selalu == ketika nilai primitifnya sama ..." - benar-benar salah. Jika Anda membuat dua objek pembungkus dengan nilai yang sama, mereka tidak akan mengembalikan true jika dibandingkan dengan ==, karena mereka adalah objek yang berbeda.
Dawood ibn Kareem
6

Cache objek integer antara -128 dan 127 dari 256 Integer

Anda tidak harus membandingkan referensi objek dengan == atau ! = . Kamu harus menggunakan . sama dengan (..) sebagai gantinya, atau lebih baik - gunakan primitif int daripada Integer.

parseInt : Mem-parsing argumen string sebagai bilangan bulat desimal yang ditandatangani. Semua karakter dalam string harus berupa angka desimal, kecuali bahwa karakter pertama mungkin berupa tanda minus ASCII '-' ('\ u002D') untuk menunjukkan nilai negatif. Nilai integer yang dihasilkan dikembalikan, persis seperti jika argumen dan radix 10 diberikan sebagai argumen untuk metode parseInt (java.lang.String, int).

valueOf Mengembalikan objek Integer memegang nilai yang diekstraksi dari String yang ditentukan ketika diuraikan dengan radix yang diberikan oleh argumen kedua. Argumen pertama ditafsirkan sebagai mewakili integer yang ditandatangani di dalam radix yang ditentukan oleh argumen kedua, persis seperti jika argumen diberikan kepada metode parseInt (java.lang.String, int). Hasilnya adalah objek Integer yang mewakili nilai integer yang ditentukan oleh string.

setara dengan

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

radix - radix yang akan digunakan dalam menafsirkan s

jadi jika Anda sama Integer.valueOf()untuk peralihan integer

-128 hingga 127 mengembalikan true dalam kondisi Anda

untuk lesser than-128 dan greater than127 itu memberifalse

Nambi
sumber
6

Untuk melengkapi jawaban yang diberikan, perhatikan juga hal-hal berikut:

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

Kode ini juga akan mencetak: false

Sebagai pengguna Jay telah mengklaim dalam komentar untuk jawaban yang diterima, kehati-hatian harus diambil ketika menggunakan operator ==pada objek, di sini Anda memeriksa apakah kedua referensi sama, yang tidak, karena mereka adalah objek yang berbeda, meskipun mereka mewakili sangat nilai yang sama. Untuk membandingkan objek, Anda harus menggunakan equals metode ini:

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

Ini akan mencetak: true

Anda mungkin bertanya, Tapi mengapa baris pertama dicetak true? . Memeriksa kode sumber untuk Integer.valueOfmetode ini, Anda dapat melihat yang berikut:

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

Jika param adalah bilangan bulat antara IntegerCache.low(default ke -128) dan IntegerCache.high(dihitung pada saat runtime dengan nilai minimum 127) maka objek yang telah dialokasikan (cache) dikembalikan. Jadi ketika Anda menggunakan 127 sebagai parameter, Anda mendapatkan dua referensi ke objek cache yang sama dan mendapatkan trueperbandingan referensi.

higuaro
sumber