&& (AND) et || (OR) dans les instructions IF

137

J'ai le code suivant:

if(!partialHits.get(req_nr).containsKey(z) || partialHits.get(req_nr).get(z) < tmpmap.get(z)){  
    partialHits.get(z).put(z, tmpmap.get(z));  
}

partialHitsest un HashMap.
Que se passera-t-il si la première affirmation est vraie? Java vérifiera-t-il toujours la deuxième instruction? Parce que pour que la première instruction soit vraie, le HashMap ne doit pas contenir la clé donnée, donc si la deuxième instruction est vérifiée, j'obtiendrai NullPointerException.
Donc en termes simples, si nous avons le code suivant

if(a && b)  
if(a || b)

Java vérifierait-il bsi aest faux dans le premier cas et si aest vrai dans le second cas?

Azimut
la source

Réponses:

202

Non, il ne sera pas évalué. Et c'est très utile. Par exemple, si vous avez besoin de tester si une chaîne n'est pas nulle ou vide, vous pouvez écrire:

if (str != null && !str.isEmpty()) {
  doSomethingWith(str.charAt(0));
}

ou, l'inverse

if (str == null || str.isEmpty()) {
  complainAboutUnusableString();
} else {
  doSomethingWith(str.charAt(0));
}

Si nous n'avions pas de «courts-circuits» en Java, nous recevrions beaucoup d'exceptions NullPointerExceptions dans les lignes de code ci-dessus.

Andreas Dolk
la source
Existe-t-il des comparaisons au niveau du bit pour que vous puissiez évaluer les deux expressions? ie si (str! = null | str.isEmpty ())? (bien sûr, ce n'est pas un exemple pratique, en fait c'est stupide, mais vous voyez l'idée)
Kezzer
5
Tant que les expressions n'ont pas d'effets secondaires, la sémantique des courts-circuits est logiquement équivalente à une évaluation complète. Autrement dit, si A est vrai, vous savez que A || B est vrai sans avoir à évaluer B. La seule fois où cela fera une différence, c'est si une expression a des effets secondaires. Comme pour les autres opérateurs, vous pouvez utiliser *et +comme logique andet or; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. Vous pouvez même le faire xor: ((A?1:0) + (B?1:0)) == 1.
sortie
1
@Kezzer: est-ce vraiment une comparaison au niveau du bit? Je pense que c'est un booleanopérateur (logique). C'est différent de l' bitwiseopérateur (entier), malgré le même symbole ...
user85421
4
Une astuce pratique lorsque vous voulez basculer entre '&&' et '||' expressions est de nier l'expression entière, de telle manière que: !(str != null && !str.isEmpty()) devient: (str !(!=) null !(&&) !(!)str.isEmpty()) et ensuite: (str == null || str.isEmpty()) parce que: d' !(!=) is == !(&&) is || !(!) eliminates itself autres négations utiles sont: !(<) is >= !(>) is <= et vice
versa
68

Java a 5 opérateurs de comparaison booléens différents: &, &&, |, ||, ^

& et && sont des opérateurs "et", | et || Opérateurs "ou", ^ est "xor"

Les simples vérifieront chaque paramètre, quelles que soient les valeurs, avant de vérifier les valeurs des paramètres. Les doubles vérifieront d'abord le paramètre de gauche et sa valeur et si true( ||) ou false( &&) laisseront le second inchangé. Son compilé? Un exemple simple devrait le rendre clair:

Donné pour tous les exemples:

 String aString = null;

ET:

 if (aString != null & aString.equals("lala"))

Les deux paramètres sont vérifiés avant la fin de l'évaluation et une exception NullPointerException sera lancée pour le deuxième paramètre.

 if (aString != null && aString.equals("lala"))

Le premier paramètre est vérifié et il retourne false, donc le deuxième paramètre ne sera pas vérifié, car le résultat est de falsetoute façon.

Idem pour OR:

 if (aString == null | !aString.equals("lala"))

Lèvera également NullPointerException.

 if (aString == null || !aString.equals("lala"))

Le premier paramètre est vérifié et il retourne true, donc le deuxième paramètre ne sera pas vérifié, car le résultat est de truetoute façon.

XOR ne peut pas être optimisé, car il dépend des deux paramètres.

Codé en dur
la source
3
"Java a 4 opérateurs de comparaison booléens différents: &, &&, |, ||" ... Vous oubliez ^(xor).
aioobe
Oh, je ne savais pas qu'il vérifie également les valeurs booléennes booléennes. Jusqu'à présent, je ne l'ai utilisé que pour les masques de bits.
codé en dur le
27

Non, il ne sera pas vérifié. Ce comportement est appelé évaluation de court-circuit et est une fonctionnalité dans de nombreux langages, y compris Java.

Peter van der Heijden
la source
20

Toutes les réponses ici sont excellentes mais, juste pour illustrer d'où cela vient, pour des questions comme celle-ci, il est bon d'aller à la source: la spécification du langage Java.

La section 15:23, Opérateur conditionnel-et (&&) , dit:

L'opérateur && est similaire à & (§15.22.2), mais n'évalue son opérande de droite que si la valeur de son opérande de gauche est vraie. [...] Au moment de l'exécution, l'expression d'opérande de gauche est évaluée en premier [...] si la valeur résultante est fausse, la valeur de l'expression conditionnelle et est fausse et l'expression d'opérande de droite n'est pas évaluée . Si la valeur de l'opérande de gauche est vraie, alors l'expression de droite est évaluée [...] la valeur résultante devient la valeur de l'expression conditionnelle et. Ainsi, && calcule le même résultat que & sur les opérandes booléens. Elle diffère uniquement en ce que l'expression d'opérande de droite est évaluée conditionnellement plutôt que toujours.

Et de même, Section 15:24, Opérateur Conditional-Or (||) , dit:

Le || l'opérateur est comme | (§15.22.2), mais n'évalue son opérande de droite que si la valeur de son opérande de gauche est fausse. [...] Au moment de l'exécution, l'expression d'opérande de gauche est évaluée en premier; [...] si la valeur résultante est vraie, la valeur de l'expression conditionnelle ou est vraie et l'expression d'opérande de droite n'est pas évaluée. Si la valeur de l'opérande de gauche est false, l'expression de droite est évaluée; [...] la valeur résultante devient la valeur de l'expression conditionnelle ou. Ainsi, || calcule le même résultat que | sur les opérandes booléens ou booléens. Elle diffère uniquement en ce que l'expression d'opérande de droite est évaluée conditionnellement plutôt que toujours.

Un peu répétitif, peut-être, mais la meilleure confirmation de leur fonctionnement exact. De même, l'opérateur conditionnel (? :) n'évalue que la 'moitié' appropriée (moitié gauche si la valeur est vraie, moitié droite si elle est fausse), permettant l'utilisation d'expressions comme:

int x = (y == null) ? 0 : y.getFoo();

sans NullPointerException.

Cowan
la source
6

Non, si a est vrai (dans un ortest), b ne sera pas testé, car le résultat du test sera toujours vrai, quelle que soit la valeur de l'expression b.

Faites un test simple:

if (true || ((String) null).equals("foobar")) {
    ...
}

ne pas jeter un NullPointerException!

Romain Linsolas
la source
6

Un court-circuit signifie ici que la deuxième condition ne sera pas évaluée.

Si (A && B) entraînera un court-circuit si A est faux.

Si (A && B) n'entraînera pas de court-circuit si A est vrai.

Si (A || B) entraînera un court-circuit si A est vrai.

Si (A || B) n'entraînera pas de court-circuit si A est faux.

user3211238
la source
4

Non, ce ne sera pas le cas, Java court-circuitera et arrêtera d'évaluer une fois qu'il connaîtra le résultat.

abyx
la source
4

Oui, l'évaluation de court-circuit pour les expressions booléennes est le comportement par défaut dans toute la famille C-like.

Un fait intéressant est que Java utilise également &et |comme opérandes logiques (ils sont surchargés, avec des inttypes, ce sont les opérations au niveau du bit attendues) pour évaluer tous les termes de l'expression, ce qui est également utile lorsque vous avez besoin des effets secondaires.

fortran
la source
Ceci est intéressant à retenir: par exemple, étant donné une méthode changeData (data) qui renvoie un booléen, alors: if (a.changeData (data) || b.changeData (data)) {doSomething (); } n'exécute pas changeData sur b si a.changeData () renvoie true, mais si (a.changeData (data) | b.changeData (data)) {doSomething ()} exécute changeData () sur a et b, même si celui invoqué sur un renvoyé true.
Sampisa
0

Cela revient à la différence fondamentale entre & et &&, | et ||

BTW vous effectuez les mêmes tâches plusieurs fois. Je ne sais pas si l'efficacité est un problème. Vous pouvez supprimer une partie de la duplication.

Z z2 = partialHits.get(req_nr).get(z); // assuming a value cannout be null.
Z z3 = tmpmap.get(z); // assuming z3 cannot be null.
if(z2 == null || z2 < z3){   
    partialHits.get(z).put(z, z3);   
} 
Peter Lawrey
la source