Back to Operators & Expressions
Q4Question 4 of 9

What are the bitwise and shift operators?

Key Concepts
  • & (AND): both bits 1 → 1. Used for masking and flag checking
  • | (OR): either bit 1 → 1. Used for combining flags
  • ^ (XOR): bits differ → 1. Used for toggling and checksums
  • ~ (NOT): flips all bits. ~0 = -1 in two's complement
  • << (left shift): multiply by 2^n. Fills with zeros on the right
  • >> (signed right shift): divide by 2^n. Preserves the sign bit
  • >>> (unsigned right shift): fills with zeros regardless of sign. Used for hash spreading

Definition

Bitwise operators (&, |, ^, ~) operate on individual bits. Shift operators (<<, >>, >>>) move bits left or right. They are used for flags, permissions, hashing, and low-level optimization.

Seven operators work on the binary representation of integers:

  • & (AND): Result bit is 1 only if both input bits are 1.
  • | (OR): Result bit is 1 if either input bit is 1.
  • ^ (XOR): Result bit is 1 if the input bits are different.
  • ~ (NOT): Flips every bit.
  • << (left shift): Shifts bits left, fills with zeros. Equivalent to multiply by 2^n.
  • >> (signed right shift): Shifts bits right, preserves the sign bit. Equivalent to divide by 2^n.
  • >>> (unsigned right shift): Shifts bits right, fills with zeros. Treats the value as unsigned.

Think of a bank's permission system where each feature is a checkbox in a row of 8 checkboxes (bits). AND checks if a specific checkbox is ticked. OR ticks a new checkbox. XOR toggles a checkbox. NOT inverts all checkboxes. Shift moves the whole row left or right.

Understanding the Concepts

How does each bitwise operator work?

JAVA
int a = 0b1100;  // 12 in binary
int b = 0b1010;  // 10 in binary

System.out.println(Integer.toBinaryString(a & b));  // 1000 (AND: both 1 → 1)
System.out.println(Integer.toBinaryString(a | b));  // 1110 (OR: either 1 → 1)
System.out.println(Integer.toBinaryString(a ^ b));  // 0110 (XOR: differ → 1)
System.out.println(Integer.toBinaryString(~a));     // ...11110011 (NOT: flip all)

Truth table:

| A | B | A & B | A \| B | A ^ B | ~A |
|---|---|-------|--------|-------|----|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 |

How are bitwise operators used for permission flags?

This is the most common real-world use in banking systems:

JAVA
// Define permission bits
final int VIEW_BALANCE    = 0b0001;  // bit 0
final int TRANSFER        = 0b0010;  // bit 1
final int INTERNATIONAL   = 0b0100;  // bit 2
final int ADMIN           = 0b1000;  // bit 3

// Rajesh has view + transfer permissions
int rajeshPerms = VIEW_BALANCE | TRANSFER;  // 0b0011 = 3

// Check if Rajesh can transfer
boolean canTransfer = (rajeshPerms & TRANSFER) != 0;  // true

// Add international permission
rajeshPerms = rajeshPerms | INTERNATIONAL;  // 0b0111 = 7

// Remove transfer permission
rajeshPerms = rajeshPerms & ~TRANSFER;  // 0b0101 = 5

// Toggle admin permission
rajeshPerms = rajeshPerms ^ ADMIN;  // 0b1101 = 13

How do shift operators work?

JAVA
// Left shift: multiply by 2^n
int amount = 100;
System.out.println(amount << 1);  // 200 (100 * 2)
System.out.println(amount << 3);  // 800 (100 * 8)

// Signed right shift: divide by 2^n, preserves sign
System.out.println(amount >> 1);   // 50 (100 / 2)
System.out.println(-100 >> 1);     // -50 (sign preserved)

// Unsigned right shift: fills with 0, ignores sign
System.out.println(-1 >>> 1);      // 2147483647 (Integer.MAX_VALUE)
System.out.println(-1 >> 1);       // -1 (sign preserved)

Why does >>> matter?

Java has no unsigned integer type. When you need to treat an int's bits as unsigned (e.g., in hash functions or protocol parsing), >>> is essential.

JAVA
// HashMap uses >>> to spread hash bits
static int hash(Object key) {
    int h = key.hashCode();
    return h ^ (h >>> 16);  // spread high bits into low bits
}

// Without >>>, negative hash codes would stay negative
// With >>>, the sign bit is treated as a data bit
int h = -12345;
System.out.println(h >> 16);   // -1 (sign extended)
System.out.println(h >>> 16);  // 65535 (zero-filled, unsigned)

What are useful bit tricks?

JAVA
// Check if a number is a power of 2
boolean isPow2 = (n > 0) && (n & (n - 1)) == 0;
// 8 = 1000, 7 = 0111, 1000 & 0111 = 0000 → true

// Fast modulo for power-of-2 divisor
int index = hashCode & (arrayLength - 1);  // same as hashCode % arrayLength
// HashMap uses this for bucket index

// Swap two variables without a temp
int x = 5, y = 10;
x = x ^ y;  // x = 15
y = x ^ y;  // y = 5
x = x ^ y;  // x = 10

What are common mistakes?

  • Confusing & with &&. & is bitwise, && is logical short-circuit. Using & in a null guard (obj != null & obj.method()) crashes.
  • Shifting by 32 or more. 1 << 32 is 1, not 0, because Java uses shift % 32 for int. Use long for shifts of 32+.
  • Forgetting ~ includes the sign bit. ~0 is -1 (all bits set in two's complement), not some large positive number.
  • Using >> instead of >>> for unsigned operations. >> preserves the sign, >>> fills with zeros. Hash spreading needs >>>.

What should a senior engineer know?

  • EnumSet is backed by a bitmask. For enums with up to 64 values, EnumSet uses a single long with bitwise operations. Very fast.
  • Bitwise operations are used in serialization formats. Protocol Buffers, Kafka wire protocol, and JDBC result set flags all use bit packing.
  • Integer.bitCount(), highestOneBit(), numberOfLeadingZeros() are hardware-accelerated on most JVMs (intrinsics).
  • Bit manipulation in financial systems is used for compact feature flags, account type encoding, and permission matrices in RBAC systems.

Visual Overview

TEXT
  Bitwise operators:
    & AND:  1100 & 1010 = 1000
    | OR:   1100 | 1010 = 1110
    ^ XOR:  1100 ^ 1010 = 0110
    ~ NOT:  ~1100 = ...0011

  Shift operators:
    100 << 1  = 200  (multiply by 2)
    100 >> 1  = 50   (divide by 2, signed)
    -1  >>> 1 = MAX  (unsigned, fill with 0)
    -1  >>  1 = -1   (signed, fill with 1)

  Permission flags:
    VIEW    = 0001     check: (perms & VIEW) != 0
    TRANSFER= 0010     add:   perms | TRANSFER
    INTL    = 0100     remove: perms & ~TRANSFER
    ADMIN   = 1000     toggle: perms ^ ADMIN

Key Takeaway

Bitwise operators work on individual bits of integers. AND (&) checks if specific bits are set. OR (|) sets bits. XOR (^) toggles bits. NOT (~) flips all bits. Left shift (<<) multiplies by powers of 2. Signed right shift (>>) divides by powers of 2 and preserves the sign. Unsigned right shift (>>>) fills with zeros and treats the value as unsigned.

In banking systems, the most common use is permission flags: each feature is a bit, and you use & to check, | to grant, and & ~mask to revoke. HashMap uses ^ and >>> to spread hash bits for better distribution. And the power-of-2 modulo trick index & (length - 1) is how HashMap computes bucket indices without division.

One sentence to remember: & checks bits, | sets bits, ^ toggles bits, << multiplies by 2, >> divides with sign, >>> divides without sign.

---

Share
Use ← → arrow keys to navigate