☕ Java

Bitwise Operators

Bitwise operators work directly on the binary representation of integers — manipulating individual bits. They're used in low-level programming, performance-critical code, flags, masks, and cryptography. Understanding them requires thinking in binary, but the patterns they enable are concise and extremely fast.

The Six Bitwise Operators

Java has six bitwise operators that operate on integer types (byte, short, int, long, char):
Java
&     // bitwise AND
|     // bitwise OR
^     // bitwise XOR (exclusive OR)
~     // bitwise NOT (complement) — unary
<<    // left shift
>>    // signed right shift (arithmetic)
>>>   // unsigned right shift (logical)

& — Bitwise AND

Bitwise AND compares each pair of bits. The result bit is 1 only if BOTH input bits are 1. Used for masking — isolating specific bits from a value.
Java
// Bit-by-bit comparison:
//   0101  (5)
// & 0011  (3)
// ──────
//   0001  (1)

System.out.println(5 & 3);    // 1
System.out.println(12 & 10);  // 8

// 12 = 1100
// 10 = 1010
// &  = 1000 = 8

// Masking — extract specific bits:
int value = 0b10110101;   // some flags byte

// Check if bit 2 (0-indexed from right) is set:
int mask = 0b00000100;    // bit 2 set
boolean bit2set = (value & mask) != 0;   // true

// Extract the lower 4 bits (nibble):
int lowerNibble = value & 0x0F;   // 0101 = 5

// Check if a number is even (bit 0 is 0 for even):
boolean isEven = (n & 1) == 0;   // faster than n % 2 == 0

// Clear specific bits (AND with mask that has 0s where you want to clear):
int flags = 0b11111111;
int cleared = flags & 0b11110111;  // clears bit 3

| — Bitwise OR

Bitwise OR compares each pair of bits. The result bit is 1 if EITHER input bit is 1. Used for setting specific bits and combining flag values.
Java
// Bit-by-bit comparison:
//   0101  (5)
// | 0011  (3)
// ──────
//   0111  (7)

System.out.println(5 | 3);    // 7
System.out.println(12 | 10);  // 14

// 12 = 1100
// 10 = 1010
// |  = 1110 = 14

// Setting specific bits:
int value = 0b10100000;
int withBit2 = value | 0b00000100;   // sets bit 2: 0b10100100

// Combining flags — common pattern for permission systems:
int READ    = 0b001;   // 1
int WRITE   = 0b010;   // 2
int EXECUTE = 0b100;   // 4

int readWrite = READ | WRITE;          // 0b011 = 3
int allPerms  = READ | WRITE | EXECUTE; // 0b111 = 7

// Check a specific permission:
boolean canRead    = (allPerms & READ)    != 0;   // true
boolean canWrite   = (allPerms & WRITE)   != 0;   // true
boolean canExecute = (allPerms & EXECUTE) != 0;   // true

// Java's own use — file permissions, Font styles:
int style = Font.BOLD | Font.ITALIC;   // combine font styles

^ — Bitwise XOR

Bitwise XOR produces 1 if the bits are different, 0 if they're the same. Used for toggling bits, simple encryption, and the classic "swap without temp variable" trick.
Java
// Bit-by-bit comparison:
//   0101  (5)
// ^ 0011  (3)
// ──────
//   0110  (6)

System.out.println(5 ^ 3);    // 6
System.out.println(12 ^ 10);  // 6

// 12 = 1100
// 10 = 1010
// ^  = 0110 = 6

// Toggle specific bits:
int value = 0b10110101;
int toggled = value ^ 0b00001111;   // flips lower 4 bits

// Toggle a single bit:
int flags = 0b10100000;
flags = flags ^ 0b00100000;   // toggles bit 5

// XOR swap (swap two integers without a temp variable):
int a = 5, b = 3;
a = a ^ b;   // a = 5^3 = 6
b = a ^ b;   // b = 6^3 = 5
a = a ^ b;   // a = 6^5 = 3
System.out.println(a);  // 3
System.out.println(b);  // 5

// XOR property: n ^ n = 0, n ^ 0 = n
// Find the one non-duplicate in an array:
int[] arr = {1, 2, 3, 2, 1};
int unique = 0;
for (int n : arr) unique ^= n;
System.out.println(unique);   // 3 — the only non-duplicate

~ — Bitwise NOT (Complement)

~ is a unary operator that inverts all bits. Every 0 becomes 1, every 1 becomes 0. The result is the bitwise complement — which in two's complement arithmetic equals -(n+1).
Java
// ~ inverts all bits:
// ~5:
//  0000...0101  (5)
//  1111...1010  (~5 = -6 in two's complement)

System.out.println(~5);    // -6
System.out.println(~0);    // -1
System.out.println(~(-1)); //  0
System.out.println(~n);    // always equals -(n + 1)

// Formula: ~n == -(n + 1)
System.out.println(~7);    // -8
System.out.println(~-8);   //  7

// Common use — creating inverse masks:
int mask = 0b00001111;     // lower 4 bits
int inverseMask = ~mask;   // 1111...11110000 — all bits EXCEPT lower 4

// Clear lower 4 bits of a value:
int value = 0b10111010;
int cleared = value & ~0x0F;   // clears lower 4 bits: 0b10110000

// ~ in loop bounds — a common trick:
// while (~(n = reader.read()) != 0) — uncommon, avoid in readable code

<< — Left Shift

<< shifts all bits to the left by a specified number of positions. Bits shifted off the left end are discarded. Zeros fill from the right. Left shifting by n is equivalent to multiplying by 2^n — and much faster.
Java
// Left shift by 1 = multiply by 2:
System.out.println(1 << 1);   // 2
System.out.println(1 << 2);   // 4
System.out.println(1 << 3);   // 8
System.out.println(1 << 10);  // 1024

// General formula: n << k == n * 2^k
System.out.println(5 << 1);   // 10 (5 * 2)
System.out.println(5 << 2);   // 20 (5 * 4)
System.out.println(3 << 3);   // 24 (3 * 8)

// Binary visualization:
// 0000 0001  (1)
// << 3
// 0000 1000  (8)

// Efficient power-of-2 sizes:
int KB = 1 << 10;   // 1024
int MB = 1 << 20;   // 1,048,576
int GB = 1 << 30;   // 1,073,741,824

// Building bitmasks:
int bit0 = 1 << 0;   // 0001
int bit1 = 1 << 1;   // 0010
int bit2 = 1 << 2;   // 0100
int bit3 = 1 << 3;   // 1000

// Overflow — shifting past the type's width wraps:
System.out.println(1 << 31);  // Integer.MIN_VALUE — sign bit set
System.out.println(1 << 32);  // 1 — wraps (shift amount % 32 for int)

>> and >>> — Right Shift

Java has two right-shift operators that differ in how they handle the sign bit. >> is arithmetic (preserves sign), >>> is logical (always fills with zeros).
Java
// >> signed right shift — fills with sign bit (0 for positive, 1 for negative):
System.out.println(16 >> 1);    // 816 / 2
System.out.println(16 >> 2);    // 416 / 4
System.out.println(-16 >> 1);   // -8 — sign bit preserved: stays negative

// General formula: n >> k == n / 2^k (for positive n)
System.out.println(100 >> 2);   // 25 (100 / 4)

// Binary visualization for >> on negative:
// 1111 1111 1111 0000  (-16)
// >> 1
// 1111 1111 1111 1000  (-8) — 1 filled from left (sign preserved)

// >>> unsigned right shift — always fills with 0 from the left:
System.out.println(16 >>> 1);     //  8 — same as >> for positive
System.out.println(-16 >>> 1);    //  2147483640 — treated as unsigned!
System.out.println(-1 >>> 1);     //  Integer.MAX_VALUE (2147483647)
System.out.println(-1 >>> 0);     // -1 — shift by 0 leaves unchanged

// Binary visualization for >>> on negative:
// 1111 1111 1111 0000  (-16 as int)
// >>> 1
// 0111 1111 1111 1000  (2147483640) — 0 filled from left

// When to use >>>:
// Working with bit patterns where sign doesn't matter
// Hash functions — combining hash codes:
int hash = (h = key.hashCode()) ^ (h >>> 16);  // HashMap's hashing strategy

// Color channel extraction:
int color = 0xFF5733AA;  // ARGB
int alpha = (color >>> 24) & 0xFF;   // 255
int red   = (color >>> 16) & 0xFF;   // 87
int green = (color >>> 8)  & 0xFF;   // 51
int blue  =  color         & 0xFF;   // 170

Practical Bit Manipulation Patterns

These patterns appear constantly in systems programming, competitive coding, and performance-critical Java code:
Java
// ── Set bit n: value | (1 << n) ─────────────────────────────────
int setBit(int value, int n) {
    return value | (1 << n);
}

// ── Clear bit n: value & ~(1 << n) ───────────────────────────────
int clearBit(int value, int n) {
    return value & ~(1 << n);
}

// ── Toggle bit n: value ^ (1 << n) ───────────────────────────────
int toggleBit(int value, int n) {
    return value ^ (1 << n);
}

// ── Check if bit n is set: (value >> n) & 1 ──────────────────────
boolean isBitSet(int value, int n) {
    return ((value >> n) & 1) == 1;
}

// ── Check if power of 2: n > 0 && (n & (n-1)) == 0 ───────────────
boolean isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0;
}
isPowerOfTwo(8);    // true1000 & 0111 = 0
isPowerOfTwo(6);    // false0110 & 0101 = 0100 != 0

// ── Count set bits (Brian Kernighan's algorithm) ──────────────────
int countBits(int n) {
    int count = 0;
    while (n != 0) {
        n &= n - 1;   // clears the lowest set bit
        count++;
    }
    return count;
}
// Or use built-in:
Integer.bitCount(255);   // 8 — all 8 bits set

// ── Get lowest set bit ────────────────────────────────────────────
int lowestSetBit = n & (-n);   // isolates the rightmost 1 bit

// ── Multiply/divide by powers of 2 ───────────────────────────────
int times8 = n << 3;    // n * 8 (faster than multiplication)
int div4   = n >> 2;    // n / 4 (faster than division, rounds toward -inf)