Comment comparer correctement deux entiers en Java?

Je sais que si vous comparez un entier primitif en boîte avec une constante telle que:

Integer a = 4; if (a < 5) 

a sera automatiquement unboxed et la comparaison fonctionnera.

Cependant, que se passe-t-il lorsque vous comparez deux Integers boîte et que vous voulez comparer l’égalité ou moins que / plus grand que?

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

Le code ci-dessus aura-t-il pour effet de vérifier s’il s’agit du même object, ou sera-t-il automatiquement désactivé dans ce cas?

Qu’en est-il de:

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

?

Non, == entre Integer, Long etc va vérifier l’ égalité de référence – c.-à-d.

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

Cela vérifiera si x et y réfèrent au même object plutôt que des objects égaux .

Alors

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

est garanti pour imprimer false . L’internalisation de “petites” valeurs autobloquées peut conduire à des résultats difficiles:

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

Cela va imprimer true , en raison des règles de la boxe ( section JLS 5.1.7 ). C’est toujours une égalité de référence utilisée, mais les références sont vraiment égales.

Personnellement, j’utiliserais:

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

ou

 if (x.equals(y)) 

Ce dernier est légèrement moins efficace – il n’y a pas de surcharge pour Integer.equals(Integer) , il va donc falloir faire une vérification de type d’exécution, alors que la première utilise le fait que nous soaps déjà que les deux objects sont Integer s.

Heureusement, compareTo connaît les types, donc:

 if (x.compareTo(y) < 0) 

devrait toujours être efficace. Bien sûr, ceci est un territoire de micro-optimisation et vous devriez utiliser le code que vous trouvez le plus clair - après vous être assuré que c'est correct 🙂

Comme vous le dites, pour toute comparaison entre un type de wrapper ( Integer , Long etc.) et un type numérique ( int , long etc.), la valeur du type de wrapper est unboxed et le test est appliqué aux valeurs primitives impliquées.

Cela se produit dans le cadre de la promotion numérique binary ( section JS 5.6.2 ). Regardez la documentation de chaque opérateur pour voir si elle est appliquée. Par exemple, depuis les documents pour == et! = ( JLS 15.21.1 ):

Si les opérandes d'un opérateur d'égalité sont tous deux de type numérique, ou si l'un est de type numérique et l'autre convertible (§5.1.8) en type numérique, une promotion numérique binary est effectuée sur les opérandes (§5.6.2).

et pour < , <=,> et> = ( JLS 15.20.1 )

Le type de chacune des opérandes d'un opérateur de comparaison numérique doit être un type convertible (§5.1.8) en type numérique primitif, ou une erreur de compilation se produit. La promotion numérique binary est effectuée sur les opérandes (§5.5.2). Si le type promu des opérandes est int ou long, une comparaison d'entier signé est effectuée; Si ce type promu est float ou double, la comparaison en virgule flottante est effectuée.

Notez que rien de tout cela n'est considéré comme faisant partie de la situation où aucun type n'est un type numérique.

== testera toujours l’égalité des objects. Il est facile d’être trompé, cependant:

 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 

Vos exemples d’inégalités fonctionneront, car ils ne sont pas définis sur les objects. Cependant, avec la comparaison == , l’égalité des objects sera toujours vérifiée. Dans ce cas, lorsque vous initialisez les objects à partir d’une primitive encadrée, le même object est utilisé (pour a et b). C’est une optimisation correcte puisque les classes de boîte primitives sont immuables.

== vérifie l’égalité de référence, cependant lors de l’écriture du code comme:

 Integer a = 1; Integer b = 1; 

Java est assez intelligent pour réutiliser le même immuable pour a et b , donc c’est vrai: a == b . Curieux, j’ai écrit un petit exemple pour montrer où java cesse d’optimiser de cette façon:

 public class BoxingLol { public static void main(Ssortingng[] 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"); } } 

Quand je comstack et lance cela (sur ma machine), j'obtiens:

 Done: 128 

Appel

 if (a == b) 

Fonctionne la plupart du temps, mais il n’est pas garanti que cela fonctionne toujours, donc ne l’utilisez pas.

La manière la plus appropriée de comparer deux classes Integer pour l’égalité, en supposant qu’elles s’appellent «a» et «b», est d’appeler:

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

Vous pouvez également utiliser cette méthode qui est légèrement plus rapide.

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

Sur ma machine, 99 milliards d’opérations ont nécessité 47 secondes avec la première méthode et 46 secondes avec la seconde méthode. Vous devriez comparer des milliards de valeurs pour voir une différence.

Notez que “a” peut être nul puisqu’il s’agit d’un object. La comparaison de cette manière ne provoquera pas une exception de pointeur nul.

Pour comparer plus et moins que, utilisez

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

Depuis Java 1.7, vous pouvez utiliser Objects.equals :

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

Retourne true si les arguments sont égaux et faux sinon. Par conséquent, si les deux arguments sont null, true est renvoyé et si exactement un argument est null, false est renvoyé. Sinon, l’égalité est déterminée en utilisant la méthode égale du premier argument.

mon avis est d’utiliser un unary + pour déclencher le unboxing sur l’un des opérandes lors de la vérification de l’égalité des valeurs, et simplement utiliser les opérateurs mathématiques autrement. Justification:

On a déjà mentionné que la comparaison == pour Integer est la comparaison d’identité, qui n’est généralement pas ce que le programmeur veut, et que le but est de faire de la comparaison de valeur; Pourtant, j’ai fait un peu de science sur la façon de faire cette comparaison le plus efficacement possible, à la fois en termes de compacité du code, de correction et de rapidité.

J’ai utilisé les méthodes habituelles:

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

et a obtenu ce code après compilation et décompilation:

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

Comme vous pouvez facilement le voir, la méthode 1 appelle Integer.equals() (évidemment), les méthodes 2-4 Integer.equals() exactement le même code , déploient les valeurs au moyen de .intValue() puis les comparent directement, et la méthode 5 ne fait que déclencher une comparaison d’identité, étant la façon incorrecte de comparer les valeurs.

Puisque (comme déjà mentionné par exemple JS) equals() (il doit faire une instanceof et une dissortingbution non vérifiée), les méthodes 2-4 fonctionneront exactement avec la même vitesse, notablement meilleure que la méthode 1 lorsqu’elles sont utilisées dans des boucles serrées. HotSpot n’est pas susceptible d’optimiser les instanceof et les instanceof .

C’est assez similaire avec d’autres opérateurs de comparaison (par exemple < / > ) - ils vont déclencher le désencapsulation, tandis que l'utilisation de compareTo() ne le fera pas - mais cette fois, l'opération est hautement optimisable car intValue() n'est qu'une méthode getter candidat à être optimisé).

À mon avis, la version 4 rarement utilisée est la manière la plus concise - chaque développeur C / Java expérimenté sait que dans la plupart des cas, un plus unaire équivaut à être .intValue() en int / .intValue() - alors que cela peut être un petit moment WTF pour certains (principalement ceux qui n'ont pas utilisé un plus unaire dans leur vie), cela montre sans aucun doute l'intention la plus claire et la plus complexe - cela montre que nous voulons une valeur int de l'un des opérandes, forçant l'autre valeur à se désinstaller également. Il est également incontestablement plus proche de la comparaison régulière i1 == i2 utilisée pour les valeurs primitives int .

Mon vote va pour i1 == +i2 & i1 > i2 style i1 > i2 pour les objects Integer , à la fois pour des raisons de performance et de cohérence. Il rend également le code portable aux primitives sans modifier autre chose que la déclaration de type. Utiliser des méthodes nommées me semble introduire un bruit sémantique, similaire au bigInt.add(10).multiply(-3) très critiqué.

cette méthode compare deux Integer avec une vérification nulle, voir les tests

 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