Definition
- Two-pointer swap: start left at 0 and right at n-1, swap
arr[left]andarr[right], increment left and decrement right until they meet. - In-place: O(n/2) swaps = O(n) time, O(1) extra space.
- Stream approach:
IntStream.rangeClosed(1, n).map(i -> arr[n - i]).toArray()reads elements from the end and builds a NEW array. - The stream approach does not mutate the source array, which is safer when the original must be preserved.
- For odd-length arrays, the middle element is not swapped because the pointers meet at the center.
A stack of bank cheques numbered [001, 002, 003, 004, 005] needs to be reversed. The two-pointer approach swaps the top and bottom cheques (001 and 005), then the second and second-to-last (002 and 004), and the middle cheque (003) stays in place.
Understanding the Concepts
How do you reverse an array in-place with two pointers?
package com.bank.util;
import java.util.Arrays;
import java.util.stream.IntStream;
public class ReverseArrayDemo {
public static void main(String[] args) {
int[] cheques = {1, 2, 3, 4, 5};
// Two-pointer in-place reversal
int left = 0;
int right = cheques.length - 1;
while (left < right) {
int temp = cheques[left];
cheques[left] = cheques[right];
cheques[right] = temp;
left++;
right--;
}
System.out.println("Reversed: " + Arrays.toString(cheques));
}
}
Reversed: [5, 4, 3, 2, 1]
How do you reverse using streams?
int[] original = {1, 2, 3, 4, 5};
int n = original.length;
// Stream approach: new array, source unchanged
int[] reversed = IntStream.rangeClosed(1, n)
.map(i -> original[n - i])
.toArray();
System.out.println("Original: " + Arrays.toString(original));
System.out.println("Reversed: " + Arrays.toString(reversed));
Original: [1, 2, 3, 4, 5]
Reversed: [5, 4, 3, 2, 1]
The stream approach produces a NEW array. The original is not mutated.
How do the two approaches compare?
| Feature | Two-pointer | Stream |
|---------|------------|--------|
| Time | O(n) | O(n) |
| Space | O(1) in-place | O(n) new array |
| Mutates source | Yes | No |
| Readability | Imperative loop | Declarative pipeline |
| Thread safety | Unsafe (mutates) | Safe (immutable result) |
What about reversing a list of strings?
import java.util.List;
import java.util.Collections;
List<String> branches = List.of("Mumbai", "Delhi", "Chennai");
// Stream approach
List<String> reversed = IntStream.rangeClosed(1, branches.size())
.mapToObj(i -> branches.get(branches.size() - i))
.toList();
// [Chennai, Delhi, Mumbai]
What are edge cases?
// Empty array: no swaps needed
int[] empty = {};
// left=0, right=-1, loop does not execute
// Single element: no swap needed
int[] single = {42};
// left=0, right=0, left < right is false, loop does not execute
// Even-length: all elements swap
int[] even = {1, 2, 3, 4};
// Swap (1,4) then (2,3). Result: [4, 3, 2, 1]
// Odd-length: middle stays
int[] odd = {1, 2, 3, 4, 5};
// Swap (1,5) then (2,4). 3 stays. Result: [5, 4, 3, 2, 1]
What about using a Stack?
import java.util.ArrayDeque;
import java.util.Deque;
// Stack approach: push all, then pop all
int[] arr = {1, 2, 3, 4, 5};
Deque<Integer> stack = new ArrayDeque<>();
for (int val : arr) stack.push(val);
for (int i = 0; i < arr.length; i++) arr[i] = stack.pop();
// [5, 4, 3, 2, 1] - O(n) time, O(n) space
What about Collections.reverse for lists?
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
List<String> mutable = new ArrayList<>(List.of("Mumbai", "Delhi", "Chennai"));
Collections.reverse(mutable);
// [Chennai, Delhi, Mumbai]
// WRONG: List.of returns unmodifiable list
// Collections.reverse(List.of("A", "B")); // UnsupportedOperationException!
What should a senior engineer know?
- The stream approach using
IntStream.rangeClosedavoids autoboxing because it stays in theIntStreampipeline and produces a primitiveint[]viatoArray(). - In banking, the non-mutating stream approach is preferred when the original order is part of the audit trail. The ledger stores balances oldest-first, but the statement shows newest-first.
Collections.reverse(list)reverses a list in place but only works on mutable lists.List.of(...)returns an unmodifiable list, soCollections.reversethrowsUnsupportedOperationException.
Visual Overview
Two-pointer approach:
[1, 2, 3, 4, 5]
L R swap(1,5)
[5, 2, 3, 4, 1]
L R swap(2,4)
[5, 4, 3, 2, 1]
LR L >= R → stop
Stream approach:
IntStream.rangeClosed(1, 5)
i=1 → arr[5-1] = arr[4] = 5
i=2 → arr[5-2] = arr[3] = 4
i=3 → arr[5-3] = arr[2] = 3
i=4 → arr[5-4] = arr[1] = 2
i=5 → arr[5-5] = arr[0] = 1
→ [5, 4, 3, 2, 1]
Key Takeaway
Reversing an array without built-in functions uses two approaches. The two-pointer swap starts from both ends and moves inward: O(n) time, O(1) space, but mutates the original. The stream approach IntStream.rangeClosed(1, n).map(i -> arr[n - i]).toArray() reads from the end and builds a new array: O(n) time, O(n) space, but preserves the original.
At National Bank of India, the ledger stores end-of-day balances oldest-first, but a statement must show newest-first. The stream approach produces the display order without altering the stored ledger, which is important for audit compliance.
Remember one sentence: two-pointer swap is O(1) space but mutates the source; IntStream index mapping produces a new reversed array without mutation.
---