Monday, June 15, 2015

Bitwise Operators

Using bitwise operators may look complicated, especially if we mixed them up with short-circuit logical operators (&&, ||). In fact, they work differently and serving different purposes. The coolest benefit that bitwise operators offering is that it allow us to specify a set of flags/setting in just a single byte. This undoubtedly important during the days where memory is costly. Even until today, some of the Java APIs are still making use of bit field such as configuration flags in java.util.regex.Pattern and operation set in java.nio.SelectionKey. Therefore, knowing how to use bitwise operators allow us to manipulate the bit field and make use of the API efficiently.

Bitwise OR |

Only return 0 when both operands are 0, otherwise return 1. This characteristic is commonly used to turn on a particular bit (to be specific, set 1 to a bit) in a bit field.

Existing bit field 1 0 1 0
|
Bit Mask 1 1 0 0
=
Outcome 1 1 1 0

The outcome will be the new bit field that override existing bit field. As you can see in the new bit field's state, the second bit in the highlighted column has been turned on.

Bitwise XOR ^

Return 0 when both operands are same, otherwise return 1. This characteristic is an extension of bitwise OR, where it can be used to turn on a bit. But it will turn off the existing bit if the bit is currently on.

Existing bit field 1 0 1 0
^
Bit Mask 1 1 0 0
=
Outcome 0 1 1 0

Again, the outcome will be the new bit field that override existing bit field. And the new bit field's state shows that first bit has been turned off and the second bit has been turned on.

Bitwise AND &

Only return 1 when both operands are 1, otherwise return 0. This characteristic is commonly used to check the state of a bit.

Existing bit field 1 0 1 0
&
Bit Mask 1 1 0 0
=
Outcome 1 0 0 0

The outcome, in this case, is not meant for overriding the existing bit field. Instead, it reveals the status of the interested bit based on the bitmask. In the example above, outcome shows that the first bit in bit field is currently on.

Bitwise NOT ~

To invert a bit. From 1 to 0, or 0 to 1. It commonly used together with bitwise AND operator to un-set/turn off a bit. ~1 = 0 ~0 = 1

Using Bitwise Operators in Java

The characteristic of each bitwise operators is clear now. Below is the examples of using bitwise operators in Java. First of all, we need a set of numeric constants. There are a few ways to declare them.

Assigning Decimal Value

public static final byte RED = 1; //0000 0001
public static final byte ORANGE = 2; //0000 0010
public static final byte YELLOW = 4; //0000 0100
public static final byte GREEN = 8; //0000 1000
public static final byte BLUE = 16; //0001 0000
public static final byte INDIGO = 32; //0010 0000
public static final byte VIOLET = 64; //0100 0000

Using Bit Shift Operator

public static final byte RED = 2 >> 1; //0000 0001
public static final byte ORANGE = 2 << 0; //0000 0010
public static final byte YELLOW = 2 << 1; //0000 0100
public static final byte GREEN = 2 << 2; //0000 1000
public static final byte BLUE = 2 << 3; //0001 0000
public static final byte INDIGO = 2 << 4; //0010 0000
public static final byte VIOLET = 2 << 5; //0100 0000

Assigning Binary Value

public static final byte RED = 0b00000001;
public static final byte ORANGE = 0b00000010;
public static final byte YELLOW = 0b00000100;
public static final byte GREEN = 0b00001000;
public static final byte BLUE = 0b00010000;
public static final byte INDIGO = 0b00100000;
public static final byte VIOLET = 0b01000000;

All 3 set of constants above are giving the same value. It is up to you to choose which one giving you the best picture. One more important point to take away is that the max amount of constant variables is depend on the max positive size of the data type. I am using byte for the example above. 1 byte equals to 8 bits and the max positive size of byte is 127. Therefore, we could only have 7 constant variables. In short, it is up to you to choose the data type that's fit your needs.

Below is the code example in using bitwise operators to manipulate bitField.

byte bitField = 0;

/**************
 * Bitwise OR 
 **************/
// Turn on ORANGE bit in bitField.
bitField |= ORANGE; 
// bitField becomes 00000010. We got ORANGE now. 
System.out.println(Integer.toBinaryString(bitField)); 

// Turn on more bits in bitField. 
bitField |= BLUE | VIOLET; 
// bitField becomes 01010010. We got VIOLET, BLUE and ORANGE now.
System.out.println(Integer.toBinaryString(bitField)); 

/**************
 * Bitwise XOR 
 **************/
// Turn on YELLOW bit in bitField.
bitField ^= YELLOW; 
// bitField becomes 01010110. We got VIOLET, BLUE, YELLOW, and ORANGE now.
System.out.println(Integer.toBinaryString(bitField)); 

// Turn off BLUE bit in bitField.
bitField ^= BLUE; 
// bitField becomes 01000110. We got VIOLET, YELLOW, and ORANGE now.
System.out.println(Integer.toBinaryString(bitField)); 

/**************
 * Bitwise AND 
 **************/
// Check if the desired bit flag is on.
boolean isExist = (bitField & ORANGE) == ORANGE ? true : false; 
// isExist = true. ORANGE is exist in the bitField.
System.out.println(isExist); 

/***********************
 * Bitwise NOT with AND 
 ***********************/
// Turn off VIOLET bit in bitField.
bitField &= ~VIOLET; 
// bitField becomes 00000110. We got YELLOW, and ORANGE now.
System.out.println(Integer.toBinaryString(bitField)); 

Disadvantages of using Bit Field

Although bit field is a useful technique, but it come along with disadvantages as below.
  • Hard to interpret bit field by reading numbers.
  • No type safe as it using constant as bit masks.
  • No easy way to iterate through the bit field. 
Since version 5, Java introduced java.util.EnumSet class which could effectively replace bit field technique and avoids from all bit field advantages mentioned above. We could use EnumSet to do the same things in the Colour bit field example above. In addition, EnumSet provides a rich set of java.util.Set functionalities, type safe, and its performance is comparable to the bit field as it internally represented in with a single long.

In conclusion, familiar with bit field technique so we could use the old Java API (those still require bit field as a parameter) properly. But moving forward, make use of EnumSet in our new methods to gain all the benefits given by EnumSet.

References:
http://www.vipan.com/htdocs/bitwisehelp.html
Book: Effective Java Second Edition by Joshua Bloch

No comments: