Definition
- Arithmetic swap – sets a to a+b, then b to a-b (which is the original a), then a to a-b (which is the original b), using addition and subtraction as reversible operations.
- XOR swap – sets a to a^b, then b to a^b (which is the original a), then a to a^b (which is the original b), using the properties x^x=0 and x^0=x.
- Aliasing danger – both methods fail if a and b refer to the same memory location, because the first operation would zero it out.
- Production recommendation – a temporary variable is preferred because the compiler optimizes it equally well, and it has no overflow or aliasing edge cases.
Imagine Rajesh and Priya each hold a sealed envelope of cash at the National Bank of India counter, and the clerk must swap the envelopes but has no spare slot on the desk. Using a clever accounting trick, the clerk combines both amounts and then subtracts back, so by the end each person holds what the other had, without ever needing an extra empty slot.
Understanding the Concepts
What is the problem?
Given two integer variables a and b, swap their values without declaring a third temporary variable. This is a classic interview exercise that tests understanding of arithmetic properties and bitwise operations.
At the National Bank of India, think of it as swapping the balances of two holding accounts in a single ledger pass without using a staging account.
package com.bank.core;
public class SwapDemo {
public static void main(String[] args) {
int a = 1000; // Rajesh's amount
int b = 250; // Priya's amount
System.out.println("Before: a=" + a + ", b=" + b);
// Swap without temp
a = a + b; // a = 1250
b = a - b; // b = 1000 (original a)
a = a - b; // a = 250 (original b)
System.out.println("After: a=" + a + ", b=" + b);
}
}
Before: a=1000, b=250
After: a=250, b=1000
How does the arithmetic method work?
The arithmetic method uses addition and subtraction. Step 1: a = a + b stores the sum in a. Step 2: b = a - b subtracts the original b from the sum, leaving b with the original a. Step 3: a = a - b subtracts the new b (original a) from the sum, leaving a with the original b.
The key insight is that addition and subtraction are inverse operations, so combining and then separating recovers both original values.
package com.bank.core;
public class ArithmeticSwap {
public static void swap(int[] arr, int i, int j) {
if (i == j) return; // guard against aliasing
arr[i] = arr[i] + arr[j]; // sum
arr[j] = arr[i] - arr[j]; // original arr[i]
arr[i] = arr[i] - arr[j]; // original arr[j]
}
public static void main(String[] args) {
int[] accounts = {50000, 75000, 30000};
System.out.println("Before: " + java.util.Arrays.toString(accounts));
swap(accounts, 0, 2);
System.out.println("After: " + java.util.Arrays.toString(accounts));
}
}
Before: [50000, 75000, 30000]
After: [30000, 75000, 50000]
How does the XOR method work?
Exclusive-or has two useful properties: x ^ x = 0 (a value XOR itself is zero) and x ^ 0 = x (a value XOR zero is itself). The three steps are a ^= b, b ^= a, a ^= b.
After step 1, a holds the combined bits. Step 2 cancels the original b bits, leaving b with the original a. Step 3 cancels the original a bits, leaving a with the original b. XOR never overflows because it operates on bits, not numeric magnitude.
package com.bank.core;
public class XorSwap {
public static void swap(int[] arr, int i, int j) {
if (i == j) return; // guard against aliasing
arr[i] ^= arr[j]; // combined bits
arr[j] ^= arr[i]; // original arr[i]
arr[i] ^= arr[j]; // original arr[j]
}
public static void main(String[] args) {
int[] amounts = {1000, 250};
System.out.println("Before: " + java.util.Arrays.toString(amounts));
swap(amounts, 0, 1);
System.out.println("After: " + java.util.Arrays.toString(amounts));
}
}
Before: [1000, 250]
After: [250, 1000]
What is the overflow danger in the arithmetic method?
If both values are large, a + b can exceed Integer.MAX_VALUE and wrap around. For fixed-width integers in Java, the final result is still correct because the later subtractions are exact inverse operations in modular arithmetic. However, the intermediate value is meaningless, which alarms code reviewers.
package com.bank.core;
public class OverflowSwap {
public static void main(String[] args) {
int a = Integer.MAX_VALUE; // 2147483647
int b = 1;
System.out.println("Before: a=" + a + ", b=" + b);
a = a + b; // overflows to -2147483648 (Integer.MIN_VALUE)
System.out.println("Intermediate a (overflowed): " + a);
b = a - b; // 2147483647 (original a, correct!)
a = a - b; // 1 (original b, correct!)
System.out.println("After: a=" + a + ", b=" + b);
}
}
Before: a=2147483647, b=1
Intermediate a (overflowed): -2147483648
After: a=1, b=2147483647
What is the aliasing danger?
Both methods break if the two variables are the same memory location. If you XOR a variable with itself, the first operation sets it to zero, and the remaining operations keep it at zero. The value is permanently lost.
package com.bank.core;
public class AliasingBug {
public static void main(String[] args) {
int[] arr = {500};
System.out.println("Before: " + arr[0]);
// WRONG: swapping index 0 with itself
int i = 0, j = 0;
arr[i] ^= arr[j]; // arr[0] = 500 ^ 500 = 0
arr[j] ^= arr[i]; // arr[0] = 0 ^ 0 = 0
arr[i] ^= arr[j]; // arr[0] = 0 ^ 0 = 0
System.out.println("After (value lost!): " + arr[0]);
}
}
Before: 500
After (value lost!): 0
How is arithmetic swap different from XOR swap?
| Property | Arithmetic (add/sub) | XOR (^) |
|----------|---------------------|---------|
| Overflow risk | Yes, intermediate overflows | No, bit-level operation |
| Works on floats | Yes | No, XOR is integer-only |
| Aliasing risk | Yes (zeroes value) | Yes (zeroes value) |
| Readability | Moderate | Low |
| Final correctness despite overflow | Yes (modular arithmetic) | N/A |
// Arithmetic: works on double (no XOR for double)
double a = 1.5, b = 2.5;
a = a + b; b = a - b; a = a - b;
// a=2.5, b=1.5
// XOR: integers only
int x = 10, y = 20;
x ^= y; y ^= x; x ^= y;
// x=20, y=10
Why should you use a temporary variable in production?
A temporary variable is equally fast after compilation, far more readable, and has no overflow or aliasing edge cases. The JVM optimizes the temporary away or uses a register. The no-temporary trick is an interview curiosity, not a production practice.
package com.bank.core;
public class TempSwap {
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] balances = {50000, 75000};
System.out.println("Before: " + java.util.Arrays.toString(balances));
swap(balances, 0, 1);
System.out.println("After: " + java.util.Arrays.toString(balances));
}
}
Before: [50000, 75000]
After: [75000, 50000]
What should a senior engineer know?
- Reversible operations. The real lesson is that addition/subtraction and XOR are reversible (invertible) operations, which is the mathematical foundation. This concept appears in cryptography, error correction, and other domains.
- Compiler optimization. Modern compilers and the JVM often use register renaming or instruction reordering to swap values without an explicit temporary, so the temporary-variable version compiles to equally efficient code.
- Array-based swaps. When swapping elements in an array (as in sorting), always guard
if (i == j) return;to prevent the aliasing bug. - BigDecimal swap. For money, you would use a temporary
BigDecimalvariable, not arithmetic tricks, becauseBigDecimaldoes not support XOR and the readability cost is even higher.
import java.math.BigDecimal;
BigDecimal temp = balanceA;
balanceA = balanceB;
balanceB = temp;
// Clear, safe, no surprises
Visual Overview
Arithmetic Swap (a=1000, b=250):
Step 1: a = a + b
┌──────┐ ┌──────┐
│ 1000 │ │ 250 │
└──────┘ └──────┘
a = 1250 b = 250
Step 2: b = a - b
a = 1250 b = 1250 - 250 = 1000
Step 3: a = a - b
a = 1250 - 1000 = 250 b = 1000
Result: a=250, b=1000 (swapped)
XOR Swap (a=1000, b=250):
Step 1: a = a ^ b
a = 1000 ^ 250 = 786 (combined bits)
b = 250
Step 2: b = a ^ b
b = 786 ^ 250 = 1000 (original a)
a = 786
Step 3: a = a ^ b
a = 786 ^ 1000 = 250 (original b)
b = 1000
Result: a=250, b=1000 (swapped)
Key Takeaway
Swapping two numbers without a temporary variable can be done two ways. The arithmetic method sets a to a plus b, then b to a minus b (which recovers the original a), then a to a minus b (which recovers the original b). The XOR method uses three exclusive-or operations, relying on the facts that a value XOR itself is zero and a value XOR zero is itself.
Both methods have real dangers. The arithmetic method can overflow in the intermediate sum, though for Java's fixed-width integers the final result is still correct because the subtractions are exact inverses in modular arithmetic. The XOR method never overflows, but both methods break under aliasing: when two variables point to the same memory location, the first operation zeroes the value and it is permanently lost.
The honest production recommendation is that a plain temporary variable is the right choice. After compilation, the JVM optimizes it equally well using register renaming, and it has none of the overflow or aliasing surprises. The no-temporary trick is an interview exercise about reversible operations, not a production best practice.
In the banking context at the National Bank of India, when swapping elements during a ledger reordering pass, I always guard with if (i == j) return to prevent the aliasing bug, and I use a temporary variable because the readability and safety matter more than saving one local variable slot.
Remember one sentence: XOR and add-subtract swap values through reversible operations, but in production a temporary variable is clearer, equally fast, and free of aliasing and overflow traps.
---