Comment comparer correctement deux entiers en Java?

je sais que si vous comparez un entier primitif boxé avec une constante telle que:

Integer a = 4;
if (a < 5)

a sera automatiquement Non boxé et la comparaison fonctionnera.

Cependant, ce qui se passe lorsque vous comparez deux en boîte Integers et souhaitez comparer les sexes ou supérieur/inférieur?

Integer a = 4;
Integer b = 5;

if (a == b)

au-dessus du code aura pour résultat de vérifier s'ils sont le même objet, ou s'il auto-unbox dans ce cas?

Ce sujet:

Integer a = 4;
Integer b = 5;

if (a < b)

?

164
demandé sur shmosel 2009-10-04 01:30:44

8 réponses

Non, = = entre entier, long etc Vérifiera pour égalité de référence - i.e.

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

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

il s'agit de vérifier si x et y se réfèrent au 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'intégration de" petites " valeurs autoboxed peut conduire à des résultats:

Integer x = 10;
Integer y = 10;

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

ce sera imprimé true , en raison des règles de boxe ( JLS section 5.1.7 ). C'est toujours l'égalité de référence qui est utilisée, mais les références vraiment 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 va devoir faire la 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 Integer .

heureusement, compareTo connaît les types, donc:

if (x.compareTo(y) < 0)

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

comme vous dites, pour toute comparaison entre un type d'emballage ( Integer , Long etc) et un type numérique ( int , long etc) la valeur du type d'emballage 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 ). Examinez la documentation de chaque opérateur pour voir si elle s'applique. Par exemple, à partir de la documentation == et != ( JLS 15.21.1 ):

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

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

Le type de chacun des opérandes d'une numérique opérateur de comparaison doit être un type qui est convertible (§5.1.8) un type numérique primitif, ou un erreur de compilation se produit. Binaire la promotion numérique est effectuée sur opérandes (§5.6.2). Si la promue le type des opérandes est int ou long, ensuite signé comparaison des entiers est si ce type de promotion est flottant ou double, puis flottant-point la comparaison est effectuée.

notez que rien de tout cela n'est considéré comme faisant partie de la situation où ni l'un ni l'autre type est un type numérique.

237
répondu Jon Skeet 2015-05-27 08:03:13

== continuera de tester l'égalité de l'objet. Il est facile de se faire avoir, 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 puisqu'ils ne sont pas définis sur des objets. Cependant, avec la comparaison == , l'égalité de l'objet sera encore 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). C'est une optimisation acceptable puisque les classes de boîtes primitives sont immuables.

38
répondu Adam Lewis 2009-10-03 21:39:49

== "vérifie l'égalité de référence, cependant en écrivant le 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 . Curieusement, 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");
    }
}

quand je compileet exécute ceci( sur ma machine), j'obtiens:

Done: 128
9
répondu Cory Kendall 2015-07-15 21:30:13

appel

if (a == b)

fonctionnera 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 façon 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 le premier méthode, et 46 secondes en utilisant la deuxième méthode. Il faudrait comparer des milliards de valeurs pour voir une différence.

notez que ' a ' peut être nul puisque c'est un objet. Comparer de cette façon ne causera pas une exception de pointeur nul.

Pour la comparaison des plus et des moins de, utiliser

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
répondu otterslide 2016-09-22 18:56:11

depuis Java 1.7, Vous pouvez utiliser les objets .égale :

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

retourne true si les arguments sont égaux et false autrement. Par conséquent, si les deux arguments sont nuls, true est retourné et si exactement un argument est nul, false est retourné. Autrement, l'égalité est déterminée en utilisant la méthode des égaux de la première argument.

7
répondu Justas 2018-03-22 14:57:04

tl;dr mon avis est d'utiliser un unary + pour déclencher la décompression sur l'un des opérandes lors de la vérification de l'égalité de valeur, et tout simplement utiliser les opérateurs mathématiques autrement. Justification:

il a déjà été mentionné que == comparaison pour Integer est la comparaison d'identité, qui n'est généralement pas ce que veut un programmeur, et que le but est de faire la comparaison de valeur; pourtant, j'ai fait un peu science sur la façon de faire cette comparaison le plus efficacement, à la fois en termes de compacité du code, l'exactitude et la vitesse.

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 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 le voir facilement, la méthode 1 appelle Integer.equals() (évidemment), les méthodes 2-4 aboutissent à exactement le même code , déballant les valeurs par moyen de .intValue() et ensuite les comparer directement, et la méthode 5 déclenche juste une comparaison d'identité, étant la mauvaise façon de comparer les valeurs.

puisque (comme déjà mentionné par exemple JS) equals() engage un overhead (il doit faire instanceof et un moulage non vérifié), les méthodes 2-4 fonctionneront avec exactement la même vitesse, remarquablement mieux que la méthode 1 lorsqu'elle est utilisée dans les boucles serrées, puisque HotSpot n'est pas susceptible d'optimiser sur les moulages & instanceof .

c'est tout à fait similaire avec d'autres opérateurs de comparaison (par exemple < / > ) - ils vont déclencher la décompression, tout en utilisant compareTo() ne sera pas - mais cette fois, l'opération est hautement optimisable par HS depuis intValue() est juste une méthode de getter (premier candidat à être optimisé).

à mon avis, la version 4 rarement utilisée est la manière la plus concise - chaque développeur C/Java chevronné sait que unary plus est dans la plupart des cas égale à mouler à int / .intValue() - alors qu'il peut être un peu WTF moment pour certains (la plupart de ceux qui n'ont pas utilisé unary plus dans leur vie), il montre sans doute l'intention la plus claire et la plus tersous - il montre que nous voulons une valeur int de l'un des opérandes, forçant l'autre valeur à unbox ainsi. Elle est aussi incontestablement la 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 pour Integer objets, à la fois pour des raisons de performance et de cohérence. Il rend également le code portable aux primitifs sans rien changer d'autre que la déclaration de type. Utiliser des méthodes nommées me semble comme introduire du bruit sémantique, similaire au style très critiqué bigInt.add(10).multiply(-3) .

6
répondu 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 . J'ai cherché un sujet similaire, Je n'ai rien trouvé d'élégant pour cela. Est venu avec un simple utilitaire de fonctions.

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
répondu JackHammer 2018-09-21 01:14:52

cette méthode Compare deux entiers avec contrôle nul, 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
répondu Alex Torson 2017-03-02 15:42:02