2

So I finally got my code to work with:

if((choice == 1 ) || (choice == 2) || (choice == 3) || (choice == 4)){

but why does:

if(choice == (1 | 2)) {

result in a logic/math error? for example, if I enter "3" then the code accepts it and processes it as successful. The code snippet is as follows:

while(counter == 0){
        try{
            int choice = Integer.parseInt(input.nextLine());

            if(choice == (1 | 2)){
                System.out.println("You got it!");
                ++counter;
            }else{
                System.out.println("Try again");
            }
        }
        catch(NumberFormatException Exception){
            System.out.println("You did something wrong");
        }


    }

And if I do:

if(choice == (1 | 2 | 3 | 4 )){

then it will only accept 7 as far as I can tell. What exactly is going on when it is compiling and is there a way to shorten the solution I found?

Paul
  • 41
  • 2
  • 1
    Binary or `|` is different from a logical or `||`. – Sirko Jan 03 '18 at 19:46
  • 4
    `|` is not a 'shorter version' of `||`, it's a different operator that does a different thing. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/opsummary.html – pvg Jan 03 '18 at 19:47
  • Google binary OR operator and all will become crystal clear. Note too that binary OR is not short-circuited. – Bathsheba Jan 03 '18 at 19:48
  • @pvg Between booleans `|` is a non-shortcircuiting boolean OR (contrary to `||` that is a shortcircuiting boolean OR). – Mark Rotteveel Jan 03 '18 at 19:49
  • @MarkRotteveel not sure why you're telling me this. – pvg Jan 03 '18 at 19:49
  • @pvg Because your comment can be read as if `|` cannot take the place of `||`, but it can. – Mark Rotteveel Jan 03 '18 at 19:52
  • You could always make a set of the things you're checking for using the streams api and check to see if it contains your choice, (`if (Stream.of(1,2,3,4).collect(Collectors.toCollection(HashSet::new)).contains(choice)) { ... }`) but it's not really shorter... – azurefrog Jan 03 '18 at 19:55
  • @Turing85 I don't see how that question would be a duplicate of this one. – Mark Rotteveel Jan 03 '18 at 19:56
  • 1
    @MarkRotteveel That question asks about the general difference between `|` and `||` and [from the second-highest answer](https://stackoverflow.com/a/96722/4216641):"*In addition, `|` can be used to perform the bitwise-OR operation on `byte`/`short`/`int`/`long` values. `||` cannot.*" – Turing85 Jan 03 '18 at 20:03
  • @MarkRotteveel aside from what Turing85 wrote about different types supported by both operators, even in case of boolean operands we can't always replace `||` with `|` and expect same results. If we have code like `if (a() || b()){...}` it will behave differently than `if (a() | b()){...}` if `a()` returns `true` (since `b()` will not be executed in firs case). – Pshemo Jan 03 '18 at 20:16
  • 1
    @Turing85 A duplicate means the **question** is a duplicate or a very near duplicate, not that one of the answers - with some mangling - also applies. – Mark Rotteveel Jan 03 '18 at 20:26
  • Possible duplicate of [Pipe (|) operator in Java](https://stackoverflow.com/questions/3312611/pipe-operator-in-java) – Bernhard Barker Jan 03 '18 at 20:44

4 Answers4

8

The issue here is that

if (choice == (1 | 2)) { ... }

doesn't mean "if choice is equal to one or equal to two, then... ." Instead, Java is interpreting 1 | 2 as "the bitwise OR of the two numbers 1 and 2," since a single vertical bar represents the bitwise OR operator. If you take the bitwise OR of 1 and 2, you get the number 3 (if you know about binary numbers, see if you can confirm why this is the case), so your if statement is completely equivalent to

if (choice == 3) { ... }

which is why you're seeing the behavior that you're seeing.

Similarly, if you write

if (choice == (1 | 2 | 3 | 4)) { ... }

Java interprets the expression 1 | 2 | 3 | 4 as "the bitwise OR of the numbers 1, 2, 3, and 4," which works out to 7.

If you have a collection of values and you want to test if a specific value is equal to any of those values, one option is, as you've noted, to have a bunch of independent equality tests. If you have a very large number of values, you may want to use something like a HashSet instead.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 3
    Why the downvote? This is a correct and a well written answer. – Bathsheba Jan 03 '18 at 19:53
  • This is way off-topic, but you might find [this SEDE query](http://data.stackexchange.com/stackoverflow/query/780210/users-by-number-of-profile-trackable-badges-obtained) interesting. – E.P. Jan 09 '18 at 14:36
3

The expression 1 | 2 is a bitwise OR, so the result is 3, and if(choice == (1 | 2)) is actually the same as if(choice == 3).

The | only acts as a logical OR between boolean values.

See also the Java Language Specification, section 15.22.1 Integer Bitwise Operators &, ^, and |:

When both operands of an operator &, ^, or | are of a type that is convertible (§5.1.8) to a primitive integral type, binary numeric promotion is first performed on the operands (§5.6.2).

The type of the bitwise operator expression is the promoted type of the operands.

[..]

For |, the result value is the bitwise inclusive OR of the operand values.

and section 15.22.2 Boolean Logical Operators &, ^, and |

When both operands of a &, ^, or | operator are of type boolean or Boolean, then the type of the bitwise operator expression is boolean. In all cases, the operands are subject to unboxing conversion (§5.1.8) as necessary.

[..]

For |, the result value is false if both operand values are false; otherwise, the result is true.

As to a shorter solution, trivially your initial code is probably as simple as it can get. If you want to scale it to a larger number of conditions, then consider:

  • 1 <= choice && choice <= 4 - if accepted values of choice are a contiguous range
  • Arrays.binarySearch(new int[1, 2, 3, 4], choice) >= 0 - requires that the array is sorted
  • Arrays.asList(1, 2, 3, 4).contains(choice) - this has overhead, because this converts the integer values to objects.
  • Convert your code to a switch (not immediately shorter, until we get pattern-matching switch in a future Java version, but can be clearer with a lot of values)

    switch(choice) {
    case 1:
    case 2:
    case 3:
    case 4:
        // if 1, 2, 3, 4
        // do things
        break;
    case 5:
        // else if 5
        // do other things
        break
    default:
        // else
        // do something else
        break;
    }
    

Except the first and the last, these options have the downside that it is not immediately clear what you are checking for. In those case, clear code trumps 'short' code.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
0

| is not a shorter version of ||.
| is a bitwise inclusive OR operator.

In this the logic result 1 if any one of bit value is 1. The result will be 0 only if both the values are 0.

for (1 | 2) for example, it gives in binary form :
0 ... 0 0 1
0 ... 0 1 0

Applying the rule it produces :
0 ... 0 1 1 -> 3

About how to simplify that :

if((choice == 1 ) || (choice == 2) || (choice == 3) || (choice == 4)){

First, parentheses between each comparison are not required as == has higher precedence than || :

if(choice == 1  || choice == 2 || choice == 3 || choice == 4){

Second, as acceptable values are contiguous, the most simple way is :

if(choice >= 1  && choice <=4){ 

Note that you could also a Set or a List that contains acceptable values in no contiguous cases :

if (Arrays.asList(1, 2, 3, 4).contains(choice)){
     ...
}

or use an IntStream :

if (IntStream.range(1,5).anyMatch(i-> i == choice)) {
 ... 
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
-1

| does binary or that is different from logical ||.

1|2 = 01|10(binary representation) = 11(binary) = 3(base10)

if you want to apply shortcut then this will help

if(choice|((2<<1)+(2<<2)+(2<<3)+(2<<4))) {
...
}
ayusha
  • 474
  • 4
  • 12