Java'daki Tamsayı sarmalayıcılarını karşılaştırırken 128 == 128 neden yanlış ama 127 == 127 doğrudur?


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

Çıktı:

false

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

Çıktı:

true

Not: -128 ile 127 arasındaki sayılar doğrudur.



1
bu soruyu sorma noktasına nasıl geldin? gerçekten eğlenceli, ama hiç kimse "gerçek dünyada" gibi bir şeye rastlamaz ... ya?
Mare Infinitus

Yanıtlar:


217

Java'da bir sayı değişmezi derleyip bir Tamsayıya (büyük harf) atadığınızda Iderleyici yayınlar:

Integer b2 =Integer.valueOf(127)

Bu kod satırı, otomatik boks kullandığınızda da oluşturulur.

valueOf belirli sayıların "havuzda toplanacağı" şekilde uygulanır ve 128'den küçük değerler için aynı örneği döndürür.

Java 1.6 kaynak kodundan satır 621:

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

Değeri highkutu sistemi özelliği ile, başka bir değere ayarlanabilir.

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

Programınızı bu sistem özelliğiyle çalıştırırsanız, çıktı doğru olur!

Açık olan sonuç: Asla özdeş olan iki referansa güvenmeyin, bunları her zaman .equals() yöntemle .

Yani b2.equals(b3) b2, b3'ün tüm mantıksal olarak eşit değerleri için true yazdırılır.

Not Integerönbellek performans nedenleriyle yoktur, bunun yerine uymak için JLS, bölüm 5.1.7 ; -128 ila 127 dahil değerler için nesne kimliği verilmelidir.

Tamsayı # valueOf (int) de bu davranışı belgeler:

bu yöntemin sıkça istenen değerleri önbelleğe alarak önemli ölçüde daha iyi alan ve zaman performansı sağlaması muhtemeldir. Bu yöntem her zaman -128 ila 127 aralığındaki değerleri dahil eder ve bu aralığın dışındaki diğer değerleri önbelleğe alabilir.


1
127'den küçük değerlerin java tarafından yok sayılacağını ve Integer.MAX_VALUE-128'den büyük değerlerin sınırlanacağını unutmayın.
Andreas Petersson

Tamsayılar Java 5 ve sonraki sürümlerde bayt değerleri için önbelleğe alınır ve yeni Tamsayı (1) == yeni Tamsayı (1) olur. Bununla birlikte, Java 1.4 veya daha düşük sürümlerde durum böyle değildir, bu nedenle sonunda bu ortama geçmeniz gerektiğine dikkat edin.
MetroidFan2002

11
hayır, bu yanlış. new Integer (1) == jvm'den bağımsız olarak new Integer (1) yanlıştır. AFAIK hiçbir derleyici "yeni" anahtar kelimede hile yapmaz. her zaman yeni bir nesne başlatmalıdır ZORUNLU.
Andreas Petersson

1
@Holger ilginç nokta. Ancak teknik olarak Integer sınıfını JDK'dan özel bir impl ile değiştirmek mümkündür ... (birisinin neden bu kadar çılgınca olacağını sormayın) - o zaman optimize etmesine izin verilmeyen yan etkileri olabilir
Andreas Petersson

1
@AndreasPetersson emin. “Derleyici”, gerçek uygulama sınıfını tam olarak bilen ve ancak yapıcı herhangi bir yan etkisi yoksa optimize edebilen JIT derleyicisi anlamına gelir. Veya ifadeyi, yalnızca yan etkileri yeniden oluşturmak için kullanın ve ardından kullanın false. Aslında, bu Kaçış Analizi ve Skaler Değiştirme uygulamasının bir yan etkisi olarak bugün olabilir.
Holger

24

Otomatik boks önbellekleri -128 ila 127. Bu, JLS'de ( 5.1.7 ) belirtilmiştir.

Kutulu p değeri doğru, yanlış, bir bayt, \ u0000 ila \ u007f aralığındaki bir karakter veya -128 ile 127 arasında bir int veya kısa sayı ise, r1 ve r2 iki boks dönüşümünün sonucu olsun s. Her zaman r1 == r2 söz konusudur.

Nesnelerle uğraşırken hatırlanması gereken basit bir kural - .equalsiki nesnenin "eşit" olup olmadığını kontrol etmek ==istiyorsanız kullanın, aynı örneğe işaret edip etmediklerini görmek istediğinizde kullanın.


1
Not: JLS, Java 9'da değişmiştir. Bu, artık yalnızca derleme zamanı sabit ifadeleri için garanti edilmektedir ; bkz. kabul edilen cevap güncellemesi.
Stephen C

9

İlkel veri türleri, ints kullanmak her iki durumda da beklenen çıktıyı üretecektir.

Ancak, Integer nesnelerini kullandığınız için == işlecinin farklı bir anlamı vardır.

Nesneler bağlamında, == değişkenlerin aynı nesne referansına atıfta bulunup bulunmadığını kontrol eder.

Nesnelerin değerini karşılaştırmak için equals () yöntemini kullanmalısınız.

 b2.equals(b1)

b2'nin b1'den küçük, daha büyük veya buna eşit olup olmadığını gösterir (ayrıntılar için API'yı kontrol edin)


7

Java ile ilgili bellek optimizasyonudur.

Belleğe kaydetmek için Java, değerleri aşağıdaki aralıklara düşen tüm sarmalayıcı nesnelerini 'yeniden kullanır':

Tüm Boolean değerleri (doğru ve yanlış)

Tüm Bayt değerleri

\ U0000 - \ u007f arasındaki tüm Karakter değerleri (yani ondalık basamakta 0 ila 127)

-128 ile 127 arasındaki tüm Kısa ve Tam Sayı değerleri.


3

Değeri -128 ile 127 arasında ise Integer.java göz at, bu yüzden önbelleğe havuzu kullanacak (Integer) 1 == (Integer) 1iken(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

Diğer cevaplar, gözlemlenen etkilerin neden gözlemlenebileceğini açıklar, ancak bu gerçekten programcılar için noktanın yanındadır (ilginç, kesinlikle, ancak gerçek kod yazarken unutmanız gereken bir şey).

Tamsayı nesnelerini eşitlikle karşılaştırmak için, equals yöntemi .

Kimlik operatörünü kullanarak Integer nesnelerini eşitlik için karşılaştırmaya çalışmayın, == ,.

Bazı eşit değerlerin özdeş nesneler olduğu görülebilir, ancak bu genellikle güvenilmesi gereken bir şey değildir.


-4

Bu sorun sadece Integer'a özgü olmadığından aşağıdakileri yazdım. Sonuç olarak, API'yi yanlış kullanırsanız, yanlış davranış gördüğünüzden daha sık değilsiniz. Doğru şekilde kullanın ve doğru davranışı görmelisiniz:

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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.