Come confrontare correttamente due interi in Java?

So che se confronti un intero primitivo in scatola con una costante come:

Integer a = 4; if (a < 5) 

a verrà automaticamente distriggersto e il confronto funzionerà.

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

 Integer a = 4; Integer b = 5; if (a == b) 

Il codice sopra riportato determinerà il controllo per vedere se si tratta dello stesso object o si annullerà automaticamente in questo caso?

Che dire:

 Integer a = 4; Integer b = 5; if (a < b) 

?

No, == tra Integer, Long ecc controllerà l’ uguaglianza di riferimento – es

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

questo controllerà se x e y riferiscono allo stesso object piuttosto che a oggetti uguali .

Così

 Integer x = new Integer(10); Integer y = new Integer(10); System.out.println(x == y); 

è garantito stampare false . L’interning di valori “piccoli” autoboxed può portare a risultati complicati:

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

Questo verrà stampato true , a causa delle regole del pugilato ( sezione JLS 5.1.7 ). È ancora utilizzata l’uguaglianza di riferimento, ma i riferimenti sono autenticamente 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 usa il fatto che sappiamo già che entrambi gli oggetti sono Integer .

Fortunatamente, compareTo conosce i tipi, quindi:

 if (x.compareTo(y) < 0) 

dovrebbe essere ancora efficiente Ovviamente, questo è un territorio di micro-ottimizzazione 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 ecc.) E un tipo numerico ( int , long etc) il valore del tipo di wrapper è unboxed e il test viene applicato ai valori primitivi coinvolti.

Ciò si verifica come parte della promozione numerica binaria ( sezione JLS 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 operatore di uguaglianza sono entrambi di tipo numerico, o uno è di tipo numerico e l'altro è convertibile (§5.1.8) in tipo numerico, la promozione numerica binaria viene eseguita sugli operandi (§5.6.2).

e per <, <=,> e> = ( JLS 15.20.1 )

Il tipo di ciascuno degli operandi di un operatore di confronto numerico deve essere un tipo convertibile (§5.1.8) in un tipo numerico primitivo o si verifica un errore in fase di compilazione. La promozione numerica binaria viene eseguita sugli operandi (§5.6.2). Se il tipo di promotore degli operandi è int o long, viene eseguito il confronto dei numeri interi con segno; se questo tipo promosso è float o double, viene eseguito il confronto a virgola mobile.

Nota come nessuno di questi è considerato come parte della situazione in cui nessuno dei due tipi è un tipo numerico.

== testerà 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 object (per entrambi a e b). Questa è una buona ottimizzazione poiché le classi di box primitive sono immutabili.

== controlla 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 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 

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 Integer per l’uguaglianza, assumendo che siano denominate ‘a’ e ‘b’ è quello di 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 impiegavano 47 secondi usando il primo metodo e 46 secondi usando il secondo metodo. Dovresti confrontare miliardi di valori per vedere eventuali differenze.

Nota che ‘a’ può essere nullo poiché è un object. Il confronto in questo modo non causerà un’eccezione del 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"); } 

Dal momento che Java 1.7 è ansible utilizzare Objects.equals :

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

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

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

È già stato detto che il confronto == per Integer è il confronto delle id quadro, che di solito non è quello che vuole un programmatore, e che lo scopo è fare un confronto di valori; ancora, ho fatto un po ‘di scienza su come fare questo confronto in modo più efficiente, sia in termini di compattezza del codice, correttezza e velocità.

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 si può facilmente vedere, il metodo 1 chiama Integer.equals() (ovviamente), i metodi 2-4 generano esattamente lo stesso codice , scartando i valori per mezzo di .intValue() e quindi confrontandoli direttamente, e il metodo 5 fa semplicemente scattare un confronto di id quadro, essendo il modo sbagliato di confrontare i valori.

Poiché (come già detto da JS) equals() incorre in overhead (deve fare instanceof e un cast non controllato), i metodi 2-4 funzioneranno esattamente con la stessa velocità, notoriamente meglio del metodo 1 quando usato in loop stretti, poiché HotSpot non è in grado di ottimizzare i cast e l’ instanceof .

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

A mio parere, la versione 4 usata raramente è il modo più conciso - ogni sviluppatore C / Java esperto sa che un unario più è nella maggior parte dei casi uguale a cast a int / .intValue() - mentre potrebbe essere un piccolo momento del WTF per alcuni (per lo più coloro che non usavano unario plus nel corso della loro vita), questo mostra chiaramente l'intento nel modo più chiaro e chiaro - mostra che vogliamo un valore int di uno degli operandi, forzando anche l'altro valore a unbox. È anche indiscutibilmente più simile al confronto i1 == i2 regolare usato per i valori int primitivi.

Il mio voto vale per i1 == +i2 e i1 > i2 style per gli oggetti Integer , sia per motivi di prestazioni che di coerenza. Rende anche il codice portatile alle primitive senza modificare nulla oltre alla dichiarazione del tipo. Usare metodi denominati mi sembra come introdurre un rumore semantico, simile allo stile bigInt.add(10).multiply(-3) molto criticato.

questo metodo confronta due interi con controllo null, vedi 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