Java XOR Operator: Syntax and Practical Uses
Explains the Java XOR operator (^) for booleans and integers, with practical patterns for bit toggling, unique-element detection, and common pitfalls.
The Java XOR operator is the exclusive OR operator, written as ^ in Java. It works on both boolean values and integral types, but the behavior differs between the two. For booleans, ^ returns true when exactly one operand is true. For integers, it performs a bitwise operation that compares each bit position independently.
The Truth Table and Boolean Behavior
For boolean operands, XOR follows a simple rule: the result is true only when the two operands have different values.
| Left | Right | Left ^ Right |
|---|---|---|
| true | true | false |
| true | false | true |
| false | true | true |
| false | false | false |
This is the same rule used in logic circuits and is the reason XOR is sometimes called the "difference detector." A common use is validation logic where exactly one condition should hold:
boolean isAdmin = true; boolean isSuperUser = false; if (isAdmin ^ isSuperUser) { // exactly one elevated role is active }
This pattern is rare in production code because != has the same truth table for booleans and is more familiar to most readers, but ^ is unambiguous when you need to enforce mutual exclusivity.
Bitwise XOR on Integral Types
When applied to integral operands, ^ compares each bit position. This includes byte, short, int, long, and char; byte, short, and char are promoted to int (or to long when the other operand is long) before the operation. A result bit is 1 when the corresponding bits of the operands differ, and 0 when they are the same.
int a = 0b1100; // 12 int b = 0b1010; // 10 int result = a ^ b; // 0b0110 = 6
Each bit pair is evaluated independently: 1^1=0, 1^0=1, 0^1=1, 0^0=0. The operation is commutative and associative, meaning the order of operands does not matter, and grouping does not matter either.
Toggling Bits in Flags and State
One of the most practical uses of XOR is toggling a specific bit without affecting others. Because x ^ 1 flips a bit and x ^ 0 leaves it unchanged, you can toggle a flag with a single operation.
int flags = 0b0010; // bit 1 is set flags ^= 0b0010; // bit 1 is now cleared flags ^= 0b0010; // bit 1 is set again
This works because XOR is its own inverse: applying the same value twice returns the original value. This property is the foundation of several algorithms, including the XOR swap trick and simple data masking.
Finding the Unique Element in an Array
A classic interview problem is finding the single element that appears once in an array where every other element appears twice. XOR solves this in linear time with constant space:
int findUnique(int[] values) { int result = 0; for (int value : values) { result ^= value; } return result; }
Because a ^ a == 0 and a ^ 0 == a, all paired elements cancel out, leaving only the unique value. This works for any integral type and does not require sorting or a hash map.
XOR Swap Without a Temporary Variable
The XOR swap exchanges two variables without a third variable:
int x = 5; int y = 9; x ^= y; y ^= x; x ^= y;
After these three operations, x holds 9 and y holds 5. The technique relies on the inverse property of XOR. It is rarely used in modern Java because the temporary-variable version is clearer, and avoiding one local is seldom an important optimization. It can still be useful in embedded or memory-constrained code where register pressure matters.
Common Mistakes and Edge Cases
A frequent mistake is confusing ^ with exponentiation. Java has no exponent operator; Math.pow() is the correct method for powers. Writing 2 ^ 10 produces 8, not 1024, because it XORs the bit patterns of 2 and 10.
Another edge case involves negative numbers. XOR operates on the two's complement representation, so the sign bit participates in the operation. For example, -1 ^ 0 is -1, and -1 ^ 1 is -2. If you are working with unsigned semantics, use Integer.toUnsignedString() to display values or store the bits in a long to avoid sign-extension surprises.
Performance and Maintainability Considerations
Bitwise XOR is a low-level operation, so the operator itself is rarely the performance bottleneck in normal application code. The real cost is maintainability: bitwise operations are compact but less readable than named constants. When using XOR for flags, define constants with descriptive names:
static final int READ = 0b001; static final int WRITE = 0b010; static final int EXECUTE = 0b100;
This keeps the intent clear while preserving the benefit of bit-level operations. In security-sensitive code, XOR is not encryption; it is reversible with the same key and should never be used for protecting data. Use it only for masking, checksums, or parity checks where its reversibility is acceptable.