67

J'ai un long jeu de comparaisons à faire en Java, et j'aimerais savoir si un ou plusieurs d'entre eux sont vrais. La chaîne de comparaisons était longue et difficile à lire, donc je l'ai cassé pour la lisibilité, et suis allé automatiquement à utiliser un opérateur de raccourci |= plutôt que negativeValue = negativeValue || boolean.Opérateur de raccourci "ou-affectation" (| =) en Java

boolean negativeValue = false; 
negativeValue |= (defaultStock < 0); 
negativeValue |= (defaultWholesale < 0); 
negativeValue |= (defaultRetail < 0); 
negativeValue |= (defaultDelivery < 0); 

Je me attends negativeValue pour être vrai si l'un de la valeur par défaut < quelque chose > valeurs sont négatives. Est-ce valide? Va-t-il faire ce que j'attends? Je ne pouvais pas le voir mentionné sur le site de Sun ou sur stackoverflow, mais Eclipse ne semble pas avoir de problème avec cela et le code se compile et s'exécute.


De même, si je voulais effectuer plusieurs intersections logiques, pourrais-je utiliser &= au lieu de &&?

+13

Pourquoi ne l'essayez-vous pas? – Roman

+0

Il s'agit d'une logique booléenne générale, pas seulement Java. donc vous pouvez le rechercher sur d'autres endroits. Et pourquoi tu ne l'essaies pas? – Dykam

+14

@Dykam: Non, il y a un comportement spécifique ici. Java * pourrait * choisir de faire | = court-circuiter, de sorte qu'il n'évaluera pas le RHS si le LHS est déjà vrai - mais ce n'est pas le cas. –

Répondre

171

Le |= est un opérateur d'affectation composé (JLS 15.26.2) pour l'opérateur logique booléen | (JLS 15.22.2); ne pas être confondu avec le conditionnel ou || (JLS 15.24). Il y a également &= et ^= correspondant à la version d'affectation de composé de la logique booléenne & et ^ respectivement.

En d'autres termes, pour boolean b1, b2, ces deux sont équivalentes:

b1 |= b2; 
b1 = b1 | b2; 

La différence entre les opérateurs logiques (& et |) par rapport à leurs homologues conditionnelles (&& et ||) est que le premier ne le font pas "court-circuit"; ce dernier fait. C'est:

  • & et |toujours évaluer les deux opérandes
  • && et || évaluer le droit opérande sous condition; l'opérande de droite n'est évalué que si sa valeur peut affecter le résultat de l'opération binaire.Cela signifie que l'opérande droit ne soit pas évalué lorsque:
    • L'opérande gauche de && évalue à false
      • (parce que peu importe ce que l'opérande de droite correspond à, l'expression entière est false)
    • L'opérande gauche de || est évaluée à true
      • (car pas m Atter ce que l'opérande de droite correspond à, l'expression entière est true)

Pour en revenir à votre question initiale, oui, cette construction est valide, et en |= est pas exactement un raccourci équivalent pour = et ||, il calcule ce que vous voulez. Puisque le côté droit de l'opérateur |= dans votre utilisation est une simple opération de comparaison d'entiers, le fait que | ne soit pas court-circuit est insignifiant.

Il existe des cas dans lesquels un court-circuit est souhaité, voire nécessaire, mais votre scénario n'en fait pas partie.

Il est regrettable que contrairement à d'autres langages, Java n'a pas &&= et ||=. Cela a été discuté dans la question Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=).

+0

+1, très approfondie. il semble plausible qu'un compilateur pourrait bien se convertir en un opérateur de court-circuit, s'il peut déterminer que l'ERS n'a pas d'effets secondaires. aucune idée à ce sujet? – Carl

+0

J'ai lu que lorsque RHS est trivial et SC n'est pas nécessaire, les opérateurs SC "intelligents" sont en fait un peu plus lent. Si vrai, alors il est plus intéressant de se demander si certains compilateurs peuvent convertir SC en NSC dans certaines circonstances. – polygenelubricants

+0

@polygenelubricants opérateurs court-circuités impliquent une branche de quelque sorte sous le capot, donc s'il n'y a pas de patron de prédiction de branchement aux valeurs de vérité utilisées avec les opérateurs et/ou l'architecture utilisée n'a pas bonne/aucune prédiction de branche (et en supposant que le compilateur et/ou la machine virtuelle n'effectuent pas eux-mêmes d'optimisations connexes), alors oui, les opérateurs SC introduiront une certaine lenteur par rapport au non-court-circuit. La meilleure façon de savoir à partir du compilateur est de compiler un programme utilisant SC vs. NSC, puis de comparer le bytecode pour voir s'il diffère. – JAB

15

Ce n'est pas un opérateur de "raccourci" (ou de court-circuit) dans la façon dont || et & & sont (en ce qu'ils n'évalueront pas le RHS s'ils connaissent déjà le résultat basé sur le LHS) mais il fera ce que vous voulez en termes de fonctionnant.

À titre d'exemple de la différence, ce code sera bien si text est nulle:

boolean nullOrEmpty = text == null || text.equals("") 

alors que ce ne sera pas:

boolean nullOrEmpty = false; 
nullOrEmpty |= text == null; 
nullOrEmpty |= text.equals(""); // Throws exception if text is null 

(vous pourriez faire "".equals(text) Il est évident que pour ce particulier cas - j'essaye juste de démontrer le principe.)

0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
             defaultRetail, defaultDelivery); 
int minParam = Collections.min (params); 
negativeValue = minParam < 0; 
+0

Je pense que je préférerais 'negativeValue = defaultStock <0 || defaultWholesale <0' etc. Mis à part l'inefficacité de toute la boxe et l'emballage qui se passent ici, je ne trouve pas aussi facile de comprendre ce que votre code voudrait vraiment dire. –

+0

I il a encore plus de 4 paramètres et le critère est le même pour chacun d'eux puis j'aime ma solution, mais pour des raisons de lisibilité je le séparerais en plusieurs lignes (ie créer Liste, trouver minvalue, comparer minvalue avec 0). – Roman

+0

Cela semble utile pour un grand nombre de comparaisons. Dans ce cas, je pense que la réduction de la clarté ne vaut pas l'économie de frappe, etc –

1

Vous pourriez juste avoir une déclaration. Exprimé sur plusieurs lignes, il se lit comme suit presque exactement comme votre code d'échantillon, seulement moins impératif:

boolean negativeValue 
    = defaultStock < 0 
    | defaultWholesale < 0 
    | defaultRetail < 0 
    | defaultDelivery < 0; 

Pour les expressions les plus simples, en utilisant | peut être plus rapide que || parce que même si elle évite de faire une comparaison, cela signifie implicitement à l'aide d'une branche et cela peut être beaucoup plus cher.

+0

J'ai commencé avec un seul, mais comme indiqué dans la question initiale, j'ai senti que "La chaîne de comparaisons était longue et difficile à lire, donc je l'ai cassé pour la lisibilité ". Cela mis à part, dans ce cas, je suis plus intéressé à apprendre le comportement de | = que de faire fonctionner ce morceau particulier de code. –

1

Bien que cela puisse être excessif pour votre problème, la bibliothèque Guava a une syntaxe agréable avec Predicate s et fait une évaluation de court-circuit de ou/et Predicate s. Essentiellement, les comparaisons sont transformées en objets, empaquetées dans une collection, puis itérées. Pour les prédicats, le premier hit vrai revient de l'itération, et vice versa pour et.

0

|| logique booléenne OU
| OU bitwise

| = opérateur OR et y compris au niveau du bit affectation

La raison pour laquelle | = ne shortcircit pas est parce qu'il ne Bitwise ou non un OU logique. C'est-à-dire:

 

C |= 2 is same as C = C | 2 

Tutorial for java operators

+0

il n'y a AUCUN OU bit à bit avec des booléens! L'opérateur '|' est un nombre entier OR ** mais est également ** OR logique - voir [Java Language Specification 15.22.2.] (Http://docs.oracle.com/javase/specs/jls/se7/html/ jls-15.html # jls-15.22.2 "JLS 15.22.2") –

+0

Vous avez raison car pour un seul bit (un booléen), les bits OU logiques sont équivalents. Bien que pratiquement, le résultat est le même. – oneklc

1

S'il est sur la lisibilité, j'ai le concept de séparation testé les données de la logique de test.Exemple de code:

// declare data 
DataType [] dataToTest = new DataType[] { 
    defaultStock, 
    defaultWholesale, 
    defaultRetail, 
    defaultDelivery 
} 

// define logic 
boolean checkIfAnyNegative(DataType [] data) { 
    boolean negativeValue = false; 
    int i = 0; 
    while (!negativeValue && i < data.length) { 
     negativeValue = data[i++] < 0; 
    } 
    return negativeValue; 
} 

Le code semble plus verbeux et explicite. Vous pouvez même créer un tableau en appel de méthode, comme ceci:

checkIfAnyNegative(new DataType[] { 
    defaultStock, 
    defaultWholesale, 
    defaultRetail, 
    defaultDelivery 
}); 

Il est plus lisible que « chaîne de comparaison », et a également l'avantage de la performance de court-circuit (au coût de l'allocation de tableau et appel de méthode).

Edit: lisibilité encore plus peut être simplement obtenue en utilisant des paramètres varargs:

signature de la méthode serait:

boolean checkIfAnyNegative(DataType ... data) 

Et l'appel pourrait ressembler à ceci:

checkIfAnyNegative(defaultStock, defaultWholesale, defaultRetail, defaultDelivery); 
+1

L'allocation de tableau et un appel de méthode sont un coût assez élevé pour court-circuiter, sauf si vous avez des opérations coûteuses dans les comparaisons (l'exemple dans la question est bon marché). Cela dit, la maintenabilité du code va la plupart du temps l'emporter sur les considérations de performance. J'utiliserais probablement quelque chose comme ça si je faisais la comparaison différemment dans un tas d'endroits différents ou si je comparais plus de 4 valeurs, mais pour un seul cas c'est un peu bavard à mon goût. –

+0

@DavidMason Je suis d'accord. Cependant, gardez à l'esprit que les calculatrices les plus modernes avaleraient ce genre de frais généraux en moins de quelques millis. Personnellement, je ne m'intéresserais pas aux frais généraux avant les problèmes de performances, ce qui [semble être raisonnable] (http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize). En outre, la verbosité du code est un avantage, en particulier lorsque javadoc n'est pas fourni ou généré par JAutodoc. –