95

I have a long set of comparisons to do in Java, and I'd like to know if one or more of them come out as true. The string of comparisons was long and difficult to read, so I broke it up for readability, and automatically went to use a shortcut operator |= rather than negativeValue = negativeValue || boolean.

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

I expect negativeValue to be true if any of the default<something> values are negative. Is this valid? Will it do what I expect? I couldn't see it mentioned on Sun's site or stackoverflow, but Eclipse doesn't seem to have a problem with it and the code compiles and runs.


Similarly, if I wanted to perform several logical intersections, could I use &= instead of &&?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
David Mason
  • 2,917
  • 4
  • 30
  • 45
  • 13
    Why don't you try it? – Roman Mar 21 '10 at 09:09
  • This is general boolean logic, not Java only. so you can look it up on other places. And why don't you just try it? – Dykam Mar 21 '10 at 09:10
  • 16
    @Dykam: No, there's specific behaviour here. Java *could* choose to make |= short-circuiting, such that it won't evaluate the RHS if the LHS is already true - but it doesn't. – Jon Skeet Mar 21 '10 at 09:14
  • Hmm, oops, I missed that one. But in this example the RHS has no side effects so that behavior doesn't play a role here besides performance, does it? – Dykam Mar 21 '10 at 09:41
  • 2
    @Jon Skeet: Short-circuiting would be appropriate for the non-existent `||=` operator, but `|=` is the combination form of the bitwise or operator. – David Thornley Mar 22 '10 at 14:07
  • @David: I wasn't trying to suggest it should happen. I was only saying that this *is* a language behavior question, not just Boolean logic. – Jon Skeet Mar 22 '10 at 14:26
  • 1
    @Jon Skeet: Sure, but making `|=` short-circuiting would be inconsistent with other compound assignment operators, since `a |= b;` would not be the same as `a = a | b;`, with the usual caveat about evaluating `a` twice (if it matters). It looks to me like the big language behavior decision was not having `||=`, so I'm missing your point. – David Thornley Mar 22 '10 at 14:59
  • @David Thornley: My first comment was rebutting Dykam's claim that the question isn't specific to Java. It is. I completely agree with the Java design decision here, but that doesn't make it any less language-specific. – Jon Skeet Mar 22 '10 at 15:30

8 Answers8

218

The |= is a compound assignment operator (JLS 15.26.2) for the boolean logical operator | (JLS 15.22.2); not to be confused with the conditional-or || (JLS 15.24). There are also &= and ^= corresponding to the compound assignment version of the boolean logical & and ^ respectively.

In other words, for boolean b1, b2, these two are equivalent:

 b1 |= b2;
 b1 = b1 | b2;

The difference between the logical operators (& and |) compared to their conditional counterparts (&& and ||) is that the former do not "shortcircuit"; the latter do. That is:

  • & and | always evaluate both operands
  • && and || evaluate the right operand conditionally; the right operand is evaluated only if its value could affect the result of the binary operation. That means that the right operand is NOT evaluated when:
    • The left operand of && evaluates to false
      • (because no matter what the right operand evaluates to, the entire expression is false)
    • The left operand of || evaluates to true
      • (because no matter what the right operand evaluates to, the entire expression is true)

So going back to your original question, yes, that construct is valid, and while |= is not exactly an equivalent shortcut for = and ||, it does compute what you want. Since the right hand side of the |= operator in your usage is a simple integer comparison operation, the fact that | does not shortcircuit is insignificant.

There are cases, when shortcircuiting is desired, or even required, but your scenario is not one of them.

It is unfortunate that unlike some other languages, Java does not have &&= and ||=. This was discussed in the question Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=).

Community
  • 1
  • 1
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • +1, very thorough. it seems plausible that a compiler might well convert to a short-circuit operator, if it could determine that the RHS has no side effects. any clue about that? – Carl Mar 22 '10 at 14:23
  • I read that when RHS is trivial and SC is not necessary, the "smart" SC operators are actually a bit slower. If true, then it's more interesting to wonder if some compilers can convert SC to NSC under certain circumstances. – polygenelubricants Mar 22 '10 at 15:08
  • @polygenelubricants short-circuited operators involve a branch of some sort under the hood, so if there's no branch-prediction-friendly pattern to the truth values used with the operators and/or the architecture in use does not have good/any branch prediction (and assuming the compiler and/or virtual machine don't do any related optimizations themselves), then yes, SC operators will introduce some slowness compared to non-short-circuiting. The best way to find out of the compiler does anything would be to compile a program using SC vs. NSC and then compare the bytecode to see if it differs. – JAB Jun 18 '14 at 19:16
  • Also, not to be confused with the bitwise OR operator which is *also* `|` – user253751 Jan 27 '15 at 09:16
16

It's not a "shortcut" (or short-circuiting) operator in the way that || and && are (in that they won't evaluate the RHS if they already know the result based on the LHS) but it will do what you want in terms of working.

As an example of the difference, this code will be fine if text is null:

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

whereas this won't:

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

(Obviously you could do "".equals(text) for that particular case - I'm just trying to demonstrate the principle.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3

You could just have one statement. Expressed over multiple lines it reads almost exactly like your sample code, only less imperative:

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

For simplest expressions, using | can be faster than || because even though it avoids doing a comparison it means using a branch implicitly and that can be many times more expensive.

blo0p3r
  • 6,790
  • 8
  • 49
  • 68
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I did start with just one, but as stated in the original question I felt that "The string of comparisons was long and difficult to read, so I broke it up for readability". That aside, in this case I'm more interested in learning the behaviour of |= than in making this particular piece of code work. – David Mason Mar 21 '10 at 21:48
  • I can't seem to be find a nice use case for this specific operator. I mean if all you care is the result why not stop where your condition are met? – Farid Dec 23 '20 at 13:11
  • 1
    @Farid sometimes you don't want to stop, say I want to detect if any of a number of tasks did something. I have a `busy` flag, just because one of my tasks did something doesn't mean I don't want to run my other tasks, but if no task is busy, I want to take an action instead. – Peter Lawrey Dec 24 '20 at 22:19
1

If it is about readability I've got the concept of separation tested data from the testing logic. Code sample:

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

The code looks more verbose and self-explanatory. You may even create an array in method call, like this:

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

It's more readable than 'comparison string', and also has performance advantage of short-circuiting (at the cost of array allocation and method call).

Edit: Even more readability can be simply achieved by using varargs parameters:

Method signature would be:

boolean checkIfAnyNegative(DataType ... data)

And the call could look like this:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );
Krzysztof Jabłoński
  • 1,890
  • 1
  • 20
  • 29
  • 1
    Array allocation and a method call are a pretty big cost for short-circuiting, unless you've got some expensive operations in the comparisons (the example in the question is cheap though). Having said that, most of the time the maintainability of the code is going to trump performance considerations. I would probably use something like this if I were doing the comparison differently in a bunch of different places or comparing more than 4 values, but for a single case it's somewhat verbose for my tastes. – David Mason Apr 25 '13 at 23:31
  • @DavidMason I agree. However keep in mind, that most modern calculators would swallow that kind of overhead in less than a few millis. Personally I wouldn't care about overhead until performance issues, which [appears to be reasonable](http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize). Also, code verbosity is an advantage, especially when javadoc is not provided or generated by JAutodoc. – Krzysztof Jabłoński Apr 26 '13 at 08:04
1

Though it might be overkill for your problem, the Guava library has some nice syntax with Predicates and does short-circuit evaluation of or/and Predicates.

Essentially, the comparisons are turned into objects, packaged into a collection, and then iterated over. For or predicates, the first true hit returns from the iteration, and vice versa for and.

Carl
  • 7,538
  • 1
  • 40
  • 64
1

It's an old post but in order to provide a different perspective for beginners, I would like give an example.

I think the most common use case for a similar compound operator would be +=. I'm sure we all wrote something like this:

int a = 10;   // a = 10
a += 5;   // a = 15

What was the point of this? The point was to avoid boilerplate and eliminate the repetitive code.

So, next line does exactly the same, avoiding to type the variable b1 twice in the same line.

b1 |= b2;
oxyt
  • 1,816
  • 3
  • 23
  • 33
  • 1
    It it harder to spot mistakes in the operation and the operator names, especially when the names are long. `longNameOfAccumulatorAVariable += 5;` vs. `longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;` – Andrei Mar 08 '19 at 09:21
0

|| logical boolean OR
| bitwise OR

|= bitwise inclusive OR and assignment operator

The reason why |= doesn't shortcircit is because it does a bitwise OR not a logical OR. That is to say:


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

Tutorial for java operators

oneklc
  • 2,659
  • 1
  • 20
  • 13
  • there is NO bitwise OR with booleans! The operator `|` is integer bitwise OR **but is also** logical OR - see [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") – user85421 Jan 11 '13 at 19:40
  • You are correct because for a single bit (a boolean) bitwise and logical OR are equivalent. Though practically, the result is the same. – oneklc Mar 20 '13 at 22:26
0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;
Roman
  • 64,384
  • 92
  • 238
  • 332
  • I think I'd prefer `negativeValue = defaultStock < 0 || defaultWholesale < 0` etc. Aside from the inefficiency of all the boxing and wrapping going on here, I don't find it nearly as easy to understand what your code would really mean. – Jon Skeet Mar 21 '10 at 09:17
  • I he has even more then 4 parameters and the criterion is the same for all of them then I like my solution, but for the sake of readability I would separate it into several lines (i.e. create List, find minvalue, compare minvalue with 0). – Roman Mar 21 '10 at 09:20
  • That does look useful for a large number of comparisons. In this case I think the reduction in clarity isn't worth the saving in typing etc. – David Mason Mar 21 '10 at 10:07