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 déballé et la comparaison fonctionnera.

Cependant, que se passe - t-il lorsque vous comparez deux Integers en boîte et que vous souhaitez comparer l'égalité ou inférieure à/supérieure à?

Integer a = 4;
Integer b = 5;

if (a == b)

Le code ci-dessus entraînera-t-il une vérification pour voir s'ils sont le même objet, ou sera-t-il auto-unbox dans ce cas?

Qu'en est-il de:

Integer a = 4;
Integer b = 5;

if (a < b)

?

Author: shmosel, 2009-10-04

8 answers

Non, = = entre Entier, Long etc vérifiera l'égalité de référence - c'est-à-dire

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

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

Cela permettra de vérifier si x et y reportez-vous à la même objet plutôt que égale objets.

Donc

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

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

Est garanti pour imprimer false. L'internement de" petites " valeurs autoboxées peut conduire à des résultats difficiles:

Integer x = 10;
Integer y = 10;

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

Cela imprimera true, en raison des règles de boxe ( JLS section 5.1.7). C'est toujours en référence à l'égalité utilisé, mais les références véritablement sont é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) donc il faudra faire une vérification de type de temps d'exécution, alors que le premier utilise le fait que nous savons déjà que les deux objets sont Integers.

Heureusement, compareTo connaît les types, donc:

if (x.compareTo(y) < 0)

Devrait toujours être efficace. Bien sûr, il s'agit d'une micro-optimisation territoire et vous devez utiliser le code que vous trouvez le plus clair-après vous être assuré qu'il est correct:)

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

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

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

Et pour et >= ( JLS 15.20.1)

Le type de chacun des opérandes d'une l'opérateur de comparaison numérique doit être un type qui est convertibles (§5.1.8) à un type numérique primitif, ou un erreur de compilation se produit. Binaire la promotion numérique est effectuée sur le opérandes (§5.6.2). Si le promu le type des opérandes est int ou long, alors la comparaison des entiers signés est effectué; si ce type promu est flotteur ou double, puis virgule flottante la comparaison est effectuée.

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

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

== testera toujours l'égalité des objets. Il est facile de se laisser berner, 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 avec des inégalités fonctionneront car ils ne sont pas définis sur des objets. Cependant, avec la comparaison ==, l'égalité des objets sera toujours vérifiée. Dans ce cas, lorsque vous initialisez les objets à partir d'une boîte primitif, le même objet est utilisé (pour a et b). Il s'agit d'une optimisation correcte car les classes de boîte primitives sont immuables.

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

== vérifie l'égalité de référence, cependant lors de l'écriture de 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(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");
    }
}

Lorsque je compile et exécute ceci (sur ma machine), j'obtiens:

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

Appel

if (a == b)

Fonctionne la plupart du temps, mais il n'est pas garanti pour toujours, donc ne l'utilisez pas.

La façon la plus appropriée de comparer deux classes entières pour l'égalité, en supposant qu'elles sont nommées ' 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 pris 47 secondes en utilisant la première méthode et 46 secondes en utilisant la deuxième méthode. Vous auriez besoin de comparer des milliards de valeurs pour voir une différence.

Notez que 'a' peut être null car c'est un objet. La comparaison de cette manière ne provoquera pas d'exception de pointeur null.

Pour comparer plus et moins de, 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");
}
 7
Author: otterslide, 2016-09-22 18:56:11

Depuis Java 1.7, vous pouvez utiliser des objets .est égal à:

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

Renvoie true si les arguments sont égaux les uns aux autres et false autrement. Par conséquent, si les deux arguments sont null, true est renvoyé et si exactement un argument est null, false est retourné. Autrement, l'égalité est déterminé en utilisant la méthode equals de la première argument.

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

Tl; dr mon avis est d'utiliser un unaire + pour déclencher le déballage sur l'un des opérandes lors de la vérification de l'égalité de valeur, et d'utiliser simplement les opérateurs mathématiques sinon. La justification suit:

Il a déjà été mentionné que == la comparaison pour Integer est une comparaison d'identité, ce qui n'est généralement pas ce qu'un programmeur veut, et que le but est de faire une comparaison de valeur; pourtant, j'ai fait un peu de science {[29] } sur la façon de faire cette comparaison le plus efficacement, terme de compacité, d'exactitude et de vitesse du code.

J'ai utilisé le tas habituel de méthodes:

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 entraînent exactement le même code, déballant les valeurs au moyen de .intValue() puis les comparant directement, et la méthode 5 déclenche simplement une comparaison d'identité, étant la mauvaise façon de comparer les valeurs.

Depuis (comme déjà mentionné par exemple par JS) equals() encourt une surcharge (il doit faire instanceof et un cast non coché), les méthodes 2-4 fonctionneront exactement à la même vitesse, remarquablement mieux que la méthode 1 lorsqu'elle est utilisée dans des boucles serrées, car HotSpot n'est pas susceptible d'optimiser les casts & instanceof.

C'est assez similaire avec d'autres opérateurs de comparaison (par exemple </>) - ils déclencheront unboxing, tout en utilisant compareTo() ne le fera pas - mais cette fois, l'opération est hautement optimisable par HS puisque intValue() est juste un méthode getter(candidat principal à optimiser).

À mon avis, la version 4 rarement utilisée est la manière la plus concise-tout développeur C/Java chevronné sait qu'unary plus est dans la plupart des cas égal à cast to int/.intValue() - bien que ce soit peut-être un petit moment WTF pour certains (principalement ceux qui n'ont pas utilisé unary plus de leur vivant), cela montre sans doute l'intention la plus claire et la plus laconique - cela montre que nous voulons une valeur int de l'un des opérandes, forçant l'autre valeur à unbox ainsi. Il est également incontestablement le plus similaire à la comparaison régulière i1 == i2 utilisée pour les valeurs primitives int.

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

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

Dans mon cas, j'ai dû comparer deux Integer s pour l'égalité où les deux pouvaient être null. Recherché sujet similaire, n'a rien trouvé d'élégant pour cela. Est venu avec une fonction utilitaire simple.

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

Cette méthode compare deux entiers avec une vérification nulle, voir 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
 -1
Author: Alex Torson, 2017-03-02 15:42:02