Topic 1.9
Bitwise and Shift Operators
In one line
Bitwise operators (&, |, ^, ~) work on the individual bits of integers, and shift operators (<<, >>, >>>) slide those bits left or right. They're how Java packs many flags into one number, tests for odd numbers and powers of two in one step, and how HashMap picks a bucket.
Think of it like this
A panel of light switches in a row, each either on (1) or off (0). An int is a panel of 32 switches. AND keeps a light on only if it's on in both panels, OR turns it on if it's on in either, XOR turns it on where the panels differ, NOT flips every switch, and a shift slides the whole pattern along the row.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Bitwise
- Working on each bit of a number separately, position by position.
- AND (&)
- A bit is 1 in the result only if it is 1 in both inputs.
- OR (|)
- A bit is 1 in the result if it is 1 in either input.
- XOR (^)
- Exclusive or: a bit is 1 in the result if the two inputs differ at that position.
- Shift
- Moving all the bits of a number left or right by some number of places.
- Mask
- A number whose 1-bits pick out the positions you care about, used with
&,|or^. - Flag
- A single bit that records yes/no for one option.
- Set bit
- A bit whose value is 1.
- Hexadecimal
- Base-16 notation, written with
0x. Each hex digit stands for exactly 4 bits:0xFFis11111111.
Step by step
01AND, OR, XOR column by column
Line the two numbers up in binary and compare each column. With 12 (1100) and 10 (1010): AND keeps columns where both are 1 → 1000 = 8. OR keeps columns where either is 1 → 1110 = 14. XOR keeps columns where they differ → 0110 = 6.
These are the same truth tables as the boolean &&, ||, ^ in Topic 1.8, applied to 32 columns at once.
02NOT and the -x - 1 rule
~12 flips all 32 bits of 0000…1100 to 1111…0011. In two's complement that's -13. In general ~x == -x - 1, which also explains why negation is ~x + 1.
03Left shift multiplies by powers of two
x << n moves every bit n places left and fills the right with zeros, which multiplies by 2ⁿ: 3 << 4 is 48. Bits pushed past position 31 are lost, so 1 << 31 is Integer.MIN_VALUE (the sign bit) and larger shifts overflow.
1 << k is the standard way to build a number with only bit k set, the building block of every mask.
04Two right shifts: >> and >>>
>> (signed) fills the left with copies of the sign bit. Positive numbers get zeros, negative numbers get ones, so the sign is kept and the result is division by 2ⁿ rounded down (toward negative infinity): -7 >> 1 is -4, while -7 / 2 is -3.
>>> (unsigned) always fills with zeros. For negative numbers the result is a big positive number: -16 >>> 28 keeps the top four 1-bits and gives 15. It's how you treat an int as 32 unsigned bits, and why (low + high) >>> 1 fixes the binary search midpoint (Topic 1.3).
05Shift distances wrap around
The JLS says only the low 5 bits of the shift distance are used for int (distance & 31) and the low 6 bits for long (distance & 63). So 1 << 32 is 1 << 0 = 1, and 1 << 40 is 1 << 8 = 256. Negative distances wrap too: 1 << -1 is 1 << 31.
This matches what x86 and ARM shift instructions do, so it's fast, but it means long mask = 1 << 40; silently produces 256. The literal 1 is an int; write 1L << 40.
06Flags in a single int
Give each option its own bit: READ = 1 << 0, WRITE = 1 << 1, EXECUTE = 1 << 2. A set of permissions is then one int: READ | WRITE is 011 = 3.
Test: (perms & WRITE) != 0. Add: perms |= EXECUTE. Remove: perms &= ~WRITE (AND with everything except that bit). Toggle: perms ^= READ.
Note the brackets in (perms & WRITE) != 0: != has higher precedence than &, so without them Java tries perms & (WRITE != 0), which doesn't compile.
07Bit tricks you'll meet in interviews and the JDK
Odd/even: (n & 1) is 1 for odd numbers, including negatives. Power of two: n > 0 && (n & (n - 1)) == 0, because a power of two has exactly one set bit, and subtracting 1 turns it into all ones below it. Lowest set bit: n & -n.
XOR pairs cancel: a ^ a == 0 and a ^ 0 == a, so XOR-ing all values where every value appears twice except one leaves that one. The DSA course (/dsa) has a bit manipulation module built on these.
HashMap: table sizes are powers of two so that hash & (n - 1) can replace the slower hash % n when picking a bucket. Topic 9.4 shows this in the real source code.
Try it yourself
- 1
Predict before you print
Work out
6 & 3,6 | 3,6 ^ 3and~6on paper (6 is110, 3 is011), then add them to the first example. (Answers: 2, 7, 5, -7.) - 2
Add a permission
Add
static final int DELETE = 1 << 3;to the flags example, grant it, and print the binary. Then test whether the user has both READ and DELETE with(perms & (READ | DELETE)) == (READ | DELETE). - 3
Find the unpaired number
XOR together 3, 9, 5, 9, 3 and print the result. Every value that appears twice cancels out, leaving 5.
Code & diagrams
`toBinaryString` drops leading zeros, so 0110 prints as 110.
Expected output
a = 1100
b = 1010
a & b = 1000 = 8
a | b = 1110 = 14
a ^ b = 110 = 6
~a = -13 (always -a - 1)
a << 2 = 48
a >> 2 = 3Expected output
n = 11111111111111111111111111110000
n >> 2 = -4
n >>> 2 = 1073741820
n >>> 28 = 15
-7 >> 1 = -4 but -7 / 2 = -3
1 << 31 = -2147483648
1 << 32 = 1
1L << 32 = 4294967296
raw = -16
raw & 0xFF = 240Expected output
perms = 11
can write? true
can execute? false
grant execute = 111
revoke write = 101
toggle read = 100Expected output
1: odd=true pow2=true bits=1 lowest=1
6: odd=false pow2=false bits=2 lowest=2
8: odd=false pow2=true bits=1 lowest=8
12: odd=false pow2=false bits=2 lowest=4
64: odd=false pow2=true bits=1 lowest=64
swapped: x=9 y=5
unpaired in 4,7,4: 7Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Shift an int by 40
Write long mask = 1 << 40; hoping for 2⁴⁰.
Break #2
Forget brackets around a mask test
Write if (flags & WRITE != 0).
Break #3
Put 0xFF into a byte
Write byte mask = 0xFF;.
Myth vs fact
Myth
>> and >>> are the same.
Fact
They differ for negative numbers: >> copies the sign bit (stays negative), >>> fills with zeros (becomes positive).
Myth
Shifting by 32 or more clears an int to zero.
Fact
The distance is taken modulo 32 for int (64 for long), so x << 32 is x.
Myth
Bit tricks always make code faster.
Fact
The JIT already turns x * 8 into a shift and x % 8 (for provably non-negative x) into a mask. Use bit operations for meaning (flags, masks), and measure before using them for speed.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Integer.bitCount,numberOfLeadingZeros,numberOfTrailingZeros,Long.bitCountand friends are HotSpot intrinsics that compile to single instructions (popcnt,lzcnt,tzcnt) on CPUs that support them. - ▸
Java 19 added
Integer.compressandInteger.expand(andLongversions), bit gather/scatter operations that map to the BMI2pext/pdepinstructions on x86. - ▸
HashMap.hash()computesh ^ (h >>> 16)before masking withn - 1, so the high bits of a poorhashCodestill influence the bucket index. That one line is why power-of-two tables are safe in practice (Topic 9.4). - ▸
java.util.BitSetstores flags in along[](64 per word) and grows as needed, andEnumSetuses a singlelongbit vector for enums with up to 64 constants, giving readable code with bitwise-level performance.
Remember this
- 1
&(AND),|(OR),^(XOR) compare two integers bit by bit, position by position.~(NOT) flips every bit; because of two's complement (Topic 1.3),~xalways equals-x - 1. - 2
<<shifts left, filling with zeros: each step doubles the number (5 << 3is 40) until bits fall off the top.>>is an arithmetic right shift that copies the sign bit in, so it halves and keeps negatives negative (-16 >> 2is -4).>>>is a logical right shift that fills with zeros, so a negative number becomes a large positive one. - 3
The shift distance is masked: for
intonly the low 5 bits of the distance are used (0–31), forlongthe low 6 (0–63). So1 << 32is 1, not 0, and1 << 40is 256. To shift beyond 31, shift along:1L << 40. - 4
Classic tricks:
(n & 1) == 0tests even;n & (n - 1)clears the lowest set bit, son > 0 && (n & (n - 1)) == 0tests for a power of two;n & -nisolates the lowest set bit;x ^ xis 0, so XOR-ing a list finds the one value without a pair. - 5
Flags: give each option one bit (
READ = 1,WRITE = 2,EXEC = 4), combine with|, test with(flags & WRITE) != 0, clear withflags &= ~WRITE, toggle withflags ^= WRITE. Unix file permissions and many network protocols are built this way; in Java code,EnumSetis the readable alternative. - 6
Bytes are signed, so a raw byte like
0xF0reads as -16.b & 0xFFgives its unsigned value 0–255, a pattern you'll use for files, network data and hashing. Helpers such asInteger.toBinaryString,Integer.bitCount,Integer.highestOneBitandInteger.numberOfTrailingZerosdo common jobs in one call (most compile to a single CPU instruction).
Explain it without notes
Explain the difference between >> and >>> with a negative number.
Why is n > 0 && (n & (n - 1)) == 0 a test for powers of two?
How do you set, clear, toggle and test one flag bit in an int?
Why does 1 << 32 evaluate to 1 in Java?
Practice
Print the number of 1-bits in 255, in 256 and in -1, using Integer.bitCount.
Store four on/off settings (bold, italic, underline, strike) in one int, turn on bold and underline, then print which are on.
Without using % or /, print whether 37 and 48 are even, and print 48 divided by 8 using a shift.
Trade-offs
- ↔
Packing flags into an
intis compact and fast to copy and compare, but it's cryptic;EnumSetgives the same performance with names and type safety. - ↔
Bit tricks shrink code and avoid branches, but they're easy to get subtly wrong (signs, precedence, shift widths) and hard to review; use them where they express the idea, with a comment.
Done when you can
I can compute
&,|,^and~by hand on small numbers.I know the difference between
<<,>>and>>>, including for negative numbers.I know shift distances are masked and use
1L <<for shifts past 31.I can set, clear, toggle and test flag bits, with correct brackets.
I can use
b & 0xFFto read a byte as unsigned and know the power-of-two test.