Come confrontare correttamente due numeri interi in Java?


So che se si confronta un numero intero primitivo in scatola con una costante come:

Integer a = 4;
if (a < 5)

a verrà automaticamente unboxed e il confronto funzionerà.

Tuttavia, cosa succede quando si confrontano due boxed Integers e si desidera confrontare l'uguaglianza o minore di/maggiore di?

Integer a = 4;
Integer b = 5;

if (a == b)

Il codice precedente provocherà il controllo per vedere se sono lo stesso oggetto, o si auto-unbox in quel caso?

Che dire di:

Integer a = 4;
Integer b = 5;

if (a < b)

?

Author: shmosel, 2009-10-04

8 answers

No, = = tra Integer, Long etc controllerà l'uguaglianza di riferimento - cioè

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

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

Questo controllerà se x e ysi riferiscono allo stesso oggetto piuttosto che oggetti uguali.

Quindi

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

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

È garantito per stampare false. L'internamento di valori autoboxed "piccoli" può portare a risultati difficili:

Integer x = 10;
Integer y = 10;

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

Questo stamperà true, a causa delle regole del pugilato ( JLS sezione 5.1.7). È ancora l'uguaglianza di riferimento in uso, ma i riferimenti veramente sono uguali.

Personalmente userei:

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

O

if (x.equals(y))

Quest'ultimo è leggermente meno efficiente - non c'è un sovraccarico per Integer.equals(Integer) quindi dovrà eseguire il controllo del tipo di tempo di esecuzione, mentre il primo utilizza il fatto che sappiamo già che entrambi gli oggetti sono Integers.

Fortunatamente, compareTo conosce i tipi, quindi:

if (x.compareTo(y) < 0)

Dovrebbe essere ancora efficiente. Naturalmente, questa è micro-ottimizzazione territorio e dovresti usare il codice che trovi più chiaro - dopo esserti assicurato che sia corretto:)

Come dici tu, per qualsiasi confronto tra un tipo di wrapper(Integer, Long etc) e un tipo numerico (int, long etc) il valore del tipo wrapper è unboxed e il test viene applicato ai valori primitivi coinvolti.

Ciò si verifica come parte della promozione numerica binaria ( JLS sezione 5.6.2). Guarda la documentazione di ogni singolo operatore per vedere se è applicata. Ad esempio, dai documenti per == e != (JLS 15.21.1):

Se gli operandi di un'uguaglianza gli operatori sono entrambi di tipo numerico, o uno è di tipo numerico e l'altro è convertibile (§5.1.8) in numerico tipo, la promozione numerica binaria è eseguito sugli operandi (§5.6.2).

E per e > = (JLS 15.20.1)

Il tipo di ciascuno degli operandi di un l'operatore di confronto numerico deve essere un tipo che è convertibile (§5.1.8) in un tipo numerico primitivo, o un si verifica un errore in fase di compilazione. Binario la promozione numerica viene eseguita sul operandi (§5.6.2). Se il promosso il tipo degli operandi è int o long, quindi il confronto di interi con segno è eseguito; se questo tipo promosso è float o double, quindi in virgola mobile viene eseguito il confronto.

Si noti come nessuno di questi è considerato come parte della situazione in cui tipo è un tipo numerico.

 237
Author: Jon Skeet, 2015-05-27 08:03:13

== verificherà ancora l'uguaglianza degli oggetti. È facile essere ingannati, tuttavia:

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

I tuoi esempi con disuguaglianze funzioneranno poiché non sono definiti sugli Oggetti. Tuttavia, con il confronto ==, l'uguaglianza degli oggetti verrà comunque verificata. In questo caso, quando si inizializzano gli oggetti da una primitiva in scatola, viene utilizzato lo stesso oggetto (sia per a che per b). Questa è un'ottimizzazione corretta poiché le classi box primitive sono immutabili.

 38
Author: Adam Lewis, 2009-10-03 21:39:49

== verifica l'uguaglianza di riferimento, tuttavia quando si scrive codice come:

Integer a = 1;
Integer b = 1;

Java è abbastanza intelligente da riutilizzare lo stesso immutabile per a e b, quindi questo è vero: a == b. Curioso, ho scritto un piccolo esempio per mostrare dove java smette di ottimizzare in questo modo:

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

Quando compilo ed eseguo questo (sulla mia macchina), ottengo:

Done: 128
 9
Author: Cory Kendall, 2015-07-15 21:30:13

Chiamata

if (a == b)

Funzionerà la maggior parte del tempo, ma non è garantito che funzioni sempre, quindi non usarlo.

Il modo più corretto per confrontare due classi intere per l'uguaglianza, supponendo che siano denominate " a " e " b " è chiamare:

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

Puoi anche usare questo modo che è leggermente più veloce.

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

Sulla mia macchina 99 miliardi di operazioni hanno richiesto 47 secondi usando il primo metodo e 46 secondi usando il secondo metodo. Dovresti confrontare miliardi di valori per vedere qualsiasi differenza.

Si noti che 'a' può essere null poiché è un Oggetto. Il confronto in questo modo non causerà un'eccezione di puntatore nullo.

Per confrontare maggiore e minore di, utilizzare

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");
}
 7
Author: otterslide, 2016-09-22 18:56:11

Da Java 1.7 è possibile utilizzare Oggetti.uguale a :

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

Restituisce true se gli argomenti sono uguali tra loro e false altrimenti. Di conseguenza, se entrambi gli argomenti sono nulli, viene restituito true e se esattamente un argomento è null, viene restituito false. Altrimenti, l'uguaglianza è determinata utilizzando il metodo equals del primo argomento.

 7
Author: Justas, 2018-03-22 14:57:04

Tl; dr la mia opinione è di usare un unario + per attivare l'unboxing su uno degli operandi quando si controlla l'uguaglianza del valore, e semplicemente usare gli operatori matematici altrimenti. La logica segue:

È già stato detto che il confronto == per Integer è un confronto di identità, che di solito non è ciò che un programmatore vuole, e che l'obiettivo è fare un confronto di valore; tuttavia, ho fatto un po ' di scienza su come fare quel confronto in modo più efficiente, sia in termine di compattezza, correttezza e velocità del codice.

Ho usato il solito gruppo di metodi:

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

E ottenuto questo codice dopo la compilazione e la decompilazione:

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

Come puoi facilmente vedere, il metodo 1 chiama Integer.equals() (ovviamente), i metodi 2-4 portano a esattamente lo stesso codice, scartando i valori per mezzo di .intValue() e quindi confrontandoli direttamente, e il metodo 5 innesca solo un confronto di identità, essendo il modo errato di confrontare i valori.

Dal (come JS) equals() incorre in un overhead (deve fare instanceof e un cast non controllato), i metodi 2-4 funzioneranno esattamente con la stessa velocità, notevolmente meglio del metodo 1 se usato in loop stretti, poiché HotSpot non è in grado di ottimizzare i cast & instanceof.

È abbastanza simile con altri operatori di confronto (ad esempio </>) - attiveranno unboxing, mentre l'utilizzo di compareTo() non lo farà - ma questa volta, l'operazione è altamente ottimizzabile da HS poiché intValue() è solo un metodo getter (candidato principale per essere ottimizzato).

, A mio parere, usate raramente la versione 4 è il più conciso modo - ogni condito C/sviluppatore Java sa che unario plus è nella maggior parte dei casi pari al cast int/.intValue() - mentre può essere un po ' WTF momento per alcuni (soprattutto quelli che non lo uso unario plus nella loro vita), probabilmente la volontà più chiaramente e più stringatamente - dimostra che ci vuole un int valore di uno dei due operandi, costringendo gli altri valore per unbox pure. È anche indiscutibilmente più simile al normale confronto i1 == i2 usato per i valori primitivi int.

Il mio voto va a favore i1 == +i2 & i1 > i2 stile per gli oggetti Integer, sia per motivi di prestazioni che di coerenza. Rende anche il codice portabile alle primitive senza modificare nulla oltre alla dichiarazione di tipo. Usare metodi con nome mi sembra di introdurre rumore semantico, simile allo stile bigInt.add(10).multiply(-3) molto criticato.

 6
Author: vaxquis, 2015-05-23 18:12:07

Nel mio caso ho dovuto confrontare due Integerper l'uguaglianza dove entrambi potrebbero essere null. Ho cercato un argomento simile, non ho trovato nulla di elegante per questo. Si avvicinò con un semplice funzioni di utilità.

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);
}
 0
Author: JackHammer, 2018-09-21 01:14:52

Questo metodo confronta due interi con il controllo null, vedere test

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
 -1
Author: Alex Torson, 2017-03-02 15:42:02