Java'da iki Tamsayı nasıl düzgün bir şekilde karşılaştırabilirim?


217

Kutulu bir ilkel tamsayı gibi bir sabit ile karşılaştırırsanız biliyorum:

Integer a = 4;
if (a < 5)

a otomatik olarak kutudan çıkarılır ve karşılaştırma çalışır.

Ancak, iki kutuyu karşılaştırdığınızda Integersve eşitliği veya daha büyük / daha küçük karşılaştırmak istediğinizde ne olur ?

Integer a = 4;
Integer b = 5;

if (a == b)

Yukarıdaki kod, aynı nesne olup olmadıklarını kontrol etmenize neden olur mu yoksa bu durumda otomatik olarak kutudan çıkarılır mı?

Ne dersin:

Integer a = 4;
Integer b = 5;

if (a < b)

?


16
Peki, denediğinde ne oldu? Ne gözlemledin?
Bart Kiers

31
@Bart Kiers: Açık bir deney, kutunun açılmasının gerçekleştiğini kanıtlamakla kalmayıp, sadece çürütebilir. Kullanmak ==yerine equalsdoğru sonuç verirse, bunun nedeni kutulu numaraların yerleştirilmesi veya başka bir şekilde yeniden kullanılması olabilir (muhtemelen derleyici optimizasyonu olarak). Bu soruyu sormanın nedeni, neler olduğunu değil, dahili olarak neler olduğunu bulmaktır. (En azından bu yüzden buradayım.)
Jim Pivarski

Hesabınıza ne oldu?

Yanıtlar:


303

Hayır, Tamsayı, Uzun vb. Arasında == referans eşitliğini kontrol eder - yani

Integer x = ...;
Integer y = ...;

System.out.println(x == y);

Bu kontrol edip olacak xve ybakınız aynı nesne yerine eşit nesneler.

Yani

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);

yazdırmayı garanti eder false. "Küçük" otomatik kutulu değerlerin yerleştirilmesi zor sonuçlara yol açabilir:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

trueBoks kuralları nedeniyle yazdırılacaktır ( JLS bölüm 5.1.7 ). Hala referans eşitlik var kullanılıyor, ancak referanslar gerçekten vardır eşit.

Kutulu p değeri, -128 ile 127 dahil (§3.10.1) arasında int türünde bir tamsayı veya doğru veya yanlış boole değişmezidir (§3.10.3) veya '\ u0000' ile '\ u007f' dahil (§3.10.4), ardından a ve b'nin p. A == b her zaman böyledir.

Şahsen ben kullanacağım:

if (x.intValue() == y.intValue())

veya

if (x.equals(y))

Demek gibi, bir sargı türü (arasında bir karşılaştırma için Integer, Longvs.) ve bir sayısal tür ( int, longvs.) sargı tipi bir değerdir kutulamasının ve test katılan ilk değerlere uygulanır.

Bu, ikili sayısal tanıtımın bir parçası olarak oluşur ( JLS bölüm 5.6.2 ). Uygulanıp uygulanmadığını görmek için her bir operatörün belgelerine bakın. Örneğin, ==ve !=( JLS 15.21.1 ) dokümanlarından :

Bir eşitlik operatörünün işlenenleri hem sayısal türde hem de biri sayısal türde ve diğeri sayısal türe dönüştürülebilirse, işlenenlerde (§5.6.2) ikili sayısal tanıtım yapılır.

ve için <, <=, >ve >=( JLS 15.20.1 )

Sayısal bir karşılaştırma operatörünün işlenenlerinin her birinin türü, ilkel sayısal türe dönüştürülebilen (§5.1.8) bir tür olmalıdır veya derleme zamanı hatası oluşur. İkili sayısal tanıtım, işlenenlerde gerçekleştirilir (§5.6.2). İşlenenlerin yükseltilen türü int veya uzunsa, imzalı tamsayı karşılaştırması gerçekleştirilir; yükseltilen bu tür float veya double ise, kayan nokta karşılaştırması yapılır.

Bunların hiçbirinin, her iki türün de sayısal bir tür olmadığı durumun bir parçası olarak değerlendirilmediğini unutmayın .


2
x.compareTo(y) < 0Bunun yerine yazmak istemesinin bir nedeni var mı x < y?
Max Nanasy

1
@ MaxNanasy: Hemen düşünebileceğimden değil.
Jon Skeet

2
Java 1.6.27+ sürümünden itibaren, Integer sınıfındaki eşitliklere aşırı yükleme olduğundan, .intValue () öğesini çağırmak kadar verimli olmalıdır. Değerleri ilkel int olarak karşılaştırır.
otterslide

@Otterslide'ın dediği gibi, bu artık Java 8'de gerekli değildir. Tamsayı ile Tamsayı karşılaştırması varsayılan değerdir.
Axel Prieto

1
@Axel: Bir aşırı yükün eklenmesi == operatörünün davranışını değiştirmez, değil mi? Şu anda test edeceğim bir konumda değilim, ama bu değişseydi çok şaşırırdım.
Jon Skeet

44

==yine de nesne eşitliğini test edecektir. Bununla birlikte, aldanması kolaydır:

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

Eşitsizliğe sahip örnekleriniz Nesneler üzerinde tanımlanmadığından çalışacaktır. Ancak, ==karşılaştırma ile, nesne eşitliği hala kontrol edilecektir. Bu durumda, nesneleri kutulu bir ilkelden başlattığınızda, aynı nesne kullanılır (hem a hem de b için). İlkel kutu sınıfları değişmez olduğu için bu iyi bir optimizasyon.


Test edilen nesne eşitliği olduğunu düşündüm. Tuhaf sonuçlar elde ettim. Bunu .equals () ile değiştirmeli miyim? Ayrıca, eşitsizlikleri oldukları gibi bırakmam gerektiğini mi yoksa başka bir şekilde de yapmam gerektiğini düşünüyor musunuz?

Otomatik bokslu bazı belirgin olmayan kenar durumlar vardır. Ben IDE (Eclipse) kırmızı kutulu bir şey renk ayarlamak için var, bu beni birkaç kez hatalardan kurtardı. İki Tamsayı karşılaştırıyorsanız, .equals kullanın, eşitsizliklerinizi netleştirmek istiyorsanız, kadroyu açıkça yazın: if ((int) c <(int) d) ...; Ayrıca şunları da yapabilirsiniz: c.compareTo (d) <0 // === c <d
Adam Lewis

12
Ve sayı değişmezlerini olarak değiştirirseniz 200, her iki test de yazdırılır false.
Daniel Earwicker

2
... çoğu JVM uygulamasında, yani. Dil özelliklerine göre sonuç uygulamalar arasında farklılık gösterebilir.
Daniel Earwicker

4
Bence bu "referans eşitliği" demek daha açık - bu şekilde ne demek istediğiniz açık. Normalde "nesne eşitliğini", " equalsçağrılmanın sonucu" anlamına gelirdi .
Jon Skeet

28

Java 1.7'den beri Objects.equals kullanabilirsiniz :

java.util.Objects.equals(oneInteger, anotherInteger);

Bağımsız değişkenler birbirine eşitse true, aksi halde false değerini döndürür. Sonuç olarak, her iki bağımsız değişken de null olursa true döndürülür ve tam olarak bir bağımsız değişken null olursa false döndürülür. Aksi takdirde, eşitlik ilk argümanın eşitleme yöntemi kullanılarak belirlenir.


Bu null'ları işler, böylece basitleştirir. Teşekkürler!
Darren Parker

10

== ancak, aşağıdaki gibi kod yazarken referans eşitliğini kontrol eder:

Integer a = 1;
Integer b = 1;

Java için aynı değişmez yeniden akıllı yeterlidir ave bbu doğrudur, böylece: a == b. Merakla, java'nın bu şekilde optimizasyonun nerede durduğunu göstermek için küçük bir örnek yazdım:

public class BoxingLol {
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            Integer a = i;
            Integer b = i;
            if (a != b) {
                System.out.println("Done: " + i);
                System.exit(0);
            }
        }
        System.out.println("Done, all values equal");
    }
}

Bunu derlediğimde ve çalıştırdığımda (makinemde) şunu elde ederim:

Done: 128

1
tl; el yıkama için dr-1; stackoverflow.com/questions/15052216/… stackoverflow.com/questions/20897020/… stackoverflow.com/questions/3131136/integers-caching-in-java vb. bahsettiğiniz konuyu ayrıntılı olarak açıklar; belgelerin (veya lib kaynağının) okunması, sonuçların yüksek konumlandırılması riski olan sahte testler oluşturmaktan daha iyidir - sadece önbelleğin alt sınırını (yani varsayılan olarak -128) tamamen unutmadınız, tek tek

ancak herhangi bir makinede aynı sonucu almanızın hiçbir garantisi yoktur - çünkü önbellek boyutunu kendiniz kolayca artırabilirsiniz , YMMV. Ayrıca OP'nin sorusu iki Tamsayı nasıl doğru bir şekilde karşılaştıracağımızdı - hiç cevaplamadınız .

Burada görüş ve algılarınıza saygı duyuyorum. Bence CS'ye temelden farklı yaklaşımlarımız var.
Cory Kendall

1
fikir ya da algı ile ilgili değil - içtenlikle kaçırdığınız gerçeklerle ilgili . Hiçbir kesin kanıt verisi olmadan (dokümanlar, kaynak vb.) Ve OP'nin sorularını cevaplamadan hiçbir şey kanıtlamayan sahte bir test yapmak ne iyi soru cevap, ne de CS olarak adlandırılmayı hak etmez. "Farklı yaklaşım" ile ilgili olarak - CS tanım gereği bir bilimdir ; bilim yaptığınız şey değildir ; Bir var yanıltıcı önemsiz (veya olurdu ilginç bir açıklama düzgün ifade takdirde) - Ne olmasını istiyorsanız bilim , Cevabınız temel hataları düzeltmek veya onları debunk makul olduğu gibi'İşler.

Elbette, kusurları ele almaya çalışacağım. Alt sınırı unutmadım, ilginç olduğunu hissetmedim ve dahil etmemeyi seçtim. Tek bir hata ile kapalı olduğuma inanmıyorum, java'nın (makinemde açıkladım, benim durumumda) bu optimizasyonu durdurma şeklini ifade ettim , ki bu 128 idi. bu doğru, sizden daha doğru cevap 127 olurdu.
Cory Kendall

8

tl; dr Bence bir tekli kullanmaktır +değeri eşitlik için denetleme işlenen birinde unboxing tetiklemek için ve sadece aksi matematik operatörleri kullanabilirsiniz. Gerekçe aşağıdaki gibidir:

O önce de bahsedildiği ==için karşılaştırma Integerbir programcı istediğini genellikle değildir ve amacı değer karşılaştırma yapmak olduğunu kimlik karşılaştırması, olduğu; yine de, hem kod kompaktlığı, doğruluk hem de hız açısından bu karşılaştırmanın en verimli şekilde nasıl yapılacağı hakkında biraz bilim yaptım .

Her zamanki yöntemleri kullandım:

public boolean method1() {
    Integer i1 = 7, i2 = 5;
    return i1.equals( i2 );
}

public boolean method2() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2.intValue();
}

public boolean method3() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2;
}

public boolean method4() {
    Integer i1 = 7, i2 = 5;
    return i1 == +i2;
}

public boolean method5() { // obviously not what we want..
    Integer i1 = 7, i2 = 5;
    return i1 == i2;
}

ve derleme ve ayrışma sonra bu kodu var:

public boolean method1() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    return var1.equals( var2 );
}

public boolean method2() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method3() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method4() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method5() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2 == var1 ) {
        return true;
    } else {
        return false;
    }
}

Kolayca görebileceğiniz gibi, yöntem 1 Integer.equals()(açıkça) yöntemlerini çağırır , yöntem 2-4, tam olarak aynı kodla sonuçlanır , değerleri kullanarak .intValue()ve sonra doğrudan karşılaştırır ve yöntem 5, bir kimlik karşılaştırmasını tetikler; değerleri karşılaştırır.

(Örn. JS tarafından daha önce belirtildiği gibi) equals()bir ek yüke maruz kaldığı için (yapması instanceofve kontrol edilmeyen bir döküm), 2-4 yöntemleri, sıkı döngülerde kullanıldığında fark edilir şekilde yöntem 1'den daha iyi olacak şekilde, aynı hızda çalışacaktır, çünkü HotSpot dökümleri optimize etme olasılığı yüksek & instanceof.

Diğer karşılaştırma operatörleriyle (ör. </ >) Oldukça benzerdir - kullanmadan kutuyu açmayı tetikleyeceklerdir compareTo()- ancak bu sefer, intValue()sadece bir alıcı yöntemi ( optimize edilmeye en iyi aday) olduğu için işlem HS tarafından oldukça optimize edilebilir.

Bence, nadiren kullanılan sürüm 4 en özlü yoludur - her tecrübeli C / Java geliştirici o tekli bilir artı çoğu durumda olduğu için döküm için eşit int/ .intValue()- biraz olabilirken WTF bazıları için moment (çoğunlukla bu yapmadım yaşamları boyunca tekli artı kullanmayın), niyeti en açık ve en tersine gösterir - intişlenenlerden birinin değerini istediğimizi , diğer değeri de kutudan çıkarmaya zorladığımızı gösterir. Ayrıca tartışmasız i1 == i2olarak ilkel intdeğerler için kullanılan düzenli karşılaştırmaya en çok benzemektedir .

Oyum , hem performans hem de tutarlılık nedenleriyle nesneler için i1 == +i2& i1 > i2stil için gidiyor Integer. Ayrıca kodu, tür bildiriminden başka bir şey değiştirmeden ilkellere taşınabilir hale getirir. Adlandırılmış yöntemleri kullanmak, çok eleştirilen bigInt.add(10).multiply(-3)stile benzer şekilde bana anlamsal gürültü katmak gibi görünüyor .


Yöntem 4'te + 'nın ne anlama geldiğini açıklayabilir misiniz? Google'ı denemeye çalıştım ama sadece bu sembolün normal kullanımlarını aldım (toplama, birleştirme).
Alex Li

1
- @AlexLi ben yazdım tam olarak ne demektir unary +, (bkz birli artı) örn stackoverflow.com/questions/2624410/...

8

çağrı

if (a == b)

Çoğu zaman çalışacaktır, ancak her zaman çalışacağı garanti edilmez, bu yüzden kullanmayın.

İki tamsayı sınıfını eşitlik için karşılaştırmanın en uygun yolu, 'a' ve 'b' olarak adlandırıldıklarını varsayarak:

if(a != null && a.equals(b)) {
  System.out.println("They are equal");
}

Biraz daha hızlı olan bu şekilde de kullanabilirsiniz.

   if(a != null && b != null && (a.intValue() == b.intValue())) {
      System.out.println("They are equal");
    } 

Makinemde 99 milyar işlem birinci yöntemi kullanarak 47 saniye, ikinci yöntemi kullanarak 46 saniye sürdü. Herhangi bir fark görmek için milyarlarca değeri karşılaştırmanız gerekir.

'A' öğesinin Nesne olduğundan boş olabileceğini unutmayın. Bu şekilde karşılaştırmak bir boş gösterici istisnasına neden olmaz.

Daha büyük ve daha küçük karşılaştırmak için

if (a != null && b!=null) {
    int compareValue = a.compareTo(b);
    if (compareValue > 0) {
        System.out.println("a is greater than b");
    } else if (compareValue < 0) {
        System.out.println("b is greater than a");
    } else {
            System.out.println("a and b are equal");
    }
} else {
    System.out.println("a or b is null, cannot compare");
}

1
if (a==b)yalnızca küçük değerler için çalışır ve çoğu zaman çalışmaz.
Tony

127'ye kadar olan tüm sayıların aynı referans değerine sahip olmasını sağlayan Java'nın varsayılan Tam Sayı önbelleği olarak 127'ye kadar çalışır. İsterseniz önbelleği 127'den yüksek olacak şekilde ayarlayabilirsiniz, ancak güvenliğiniz için == kullanmayın.
otterslide

3

İki tamsayı karşılaştırması için daima equals () yöntemini kullanmalıyız. Önerilen uygulama budur.

JVM'nin dahili optimizasyonu nedeniyle == kullanarak iki tamsayıyı belirli tamsayı değerleri aralığı için (-128 ila 127 arasında tam sayı) çalıştıracak şekilde karşılaştırırsak.

Lütfen örneklere bakın:

Dava 1:

Tam sayı a = 100; Tam sayı b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Yukarıdaki durumda JVM önbelleğe havuzundan bir değer ve b kullanır ve aynı nesne örneği tam sayı nesnesinin (dolayısıyla hafıza adresi) geri döner ve her ikisi de bir equal.Its olan elde JVM optimizasyon belirli aralıktaki değerleri için yapar.

Durum 2: Bu durumda, a ve b eşit değildir, çünkü -128 ila 127 arasındadır.

Tam sayı a = 220; Tam sayı b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Doğru yol:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true

Umarım bu yardımcı olur.


1

Benim durumumda Integer, her ikisinin de olabileceği iki eşitliği karşılaştırmak zorunda kaldım null. Benzer bir konu aradı, bunun için zarif bir şey bulamadı. Basit bir yardımcı program fonksiyonları ile geldi.

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

//considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}

-1

Çünkü karşılaştırma yöntemi, sağ operatörle int (x == y) veya sınıf Integer (x.equals (y)) türüne göre yapılmalıdır.

public class Example {

    public static void main(String[] args) {
     int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1]!=arr[j]) && (arr[j]!=arr[j+1])) 
                System.out.println("int>"+arr[j]);


    Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1]))) 
                System.out.println("Interger>"+I_arr[j]);
    }
}

-2

bu yöntem iki Tamsayıyı null denetimi ile karşılaştırır, testlere bakın

public static boolean compare(Integer int1, Integer int2) {
    if(int1!=null) {
        return int1.equals(int2);
    } else {
        return int2==null;
    }
    //inline version:
    //return (int1!=null) ? int1.equals(int2) : int2==null;
}

//results:
System.out.println(compare(1,1));           //true
System.out.println(compare(0,1));           //false
System.out.println(compare(1,0));           //false
System.out.println(compare(null,0));        //false
System.out.println(compare(0,null));        //false
System.out.println(compare(null,null));     //true

4
Objects.equals(x,y)Bunun için kendi yönteminizi kullanmak yerine yöntemi kullanmak daha iyi olur diye düşünüyorum .
ryvantage
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.