Arrays · Core Array Techniques

Array Hashing

Using a hash map to trade O(n) extra space for turning an O(n^2) lookup problem into O(n).

  • 5 min read
  • 11 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: prefix XOR + hash map
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Array hashing trades memory for time. As you walk the array once, you store what you've seen in a hash map or set. Then "have I seen the value that completes this one?" is an O(1) lookup instead of a rescan. It's the move behind Two Sum, finding duplicates and counting frequencies. With a running XOR or sum as the key, it also counts subarrays.

We'll go from simple to more technical:

  1. The pattern: check the map, then insert.
  2. A second flavor: prefix XOR with a hash map.
  3. Where the same lookup-instead-of-rescan runs in production.
  4. Choosing keys and values, and when sorting plus two pointers is the better trade.

The approach

Checking every pair for a target sum is O(n^2): each element scans all the later ones. You don't need to search for the partner, though. You only need to remember what you've already seen. For each element, work out its complement, target - value. Look it up in the map. Then store the element with its index.

Find two values that add up to 9 in [5, 8, 3, 6]:

IndexValueComplementIn the map?Map after
054no{5: 0}
181no{5: 0, 8: 1}
236no{5: 0, 8: 1, 3: 2}
363yes, at index 2found: indexes 2 and 3

Java

Map<Integer, Integer> seen = new HashMap<>();  // value -> index
for (int i = 0; i < nums.length; i++) {
    int complement = target - nums[i];
    if (seen.containsKey(complement)) {
        // found: seen.get(complement) and i
    }
    seen.put(nums[i], i);
}

Python

seen = {}                          # value -> index
for i, x in enumerate(nums):
    complement = target - x
    if complement in seen:
        pass                       # found: seen[complement] and i
    seen[x] = i

Check first, then insert. That way an element can never pair with itself, with no extra check needed.

A second flavor: prefix XOR + hash map

Some questions count instead of find. Count the subarrays whose XOR equals k keeps a running XOR and a map of how many times each XOR value has appeared so far:

Java

Map<Integer, Integer> countAtXor = new HashMap<>();
countAtXor.put(0, 1);              // an empty prefix (XOR of nothing) is 0
int runningXor = 0, count = 0;
for (int i = 0; i < nums.length; i++) {
    runningXor ^= nums[i];
    count += countAtXor.getOrDefault(runningXor ^ k, 0);
    countAtXor.merge(runningXor, 1, Integer::sum);
}

Python

from collections import Counter

count_at_xor = Counter({0: 1})     # an empty prefix (XOR of nothing) is 0
running_xor = count = 0
for x in nums:
    running_xor ^= x
    count += count_at_xor[running_xor ^ k]
    count_at_xor[running_xor] += 1

XOR undoes itself: a ^ a = 0. So if an earlier prefix had XOR runningXor ^ k, the elements between that prefix and here XOR to exactly k. Every such earlier prefix is a separate answer, which is why the map stores a count rather than one index. In [5, 3, 6, 5] with k = 6, it counts two subarrays: [5, 3] and [6].

Where you'll see it

"Has this webhook event ID been processed before?" is the same lookup. So is "have we seen this user or IP in the last N requests?" A cache is built on the same trade: spend memory to avoid repeating expensive work.

Which tool for which job?

You needUse
"Have I seen x before?"a hash set
The index (or count) of what you've seena hash map
Counts of small integers (0..k)a plain array of counts
A pair summing to a target, original indicesone pass: check, then insert
Subarrays with a given sum or XORrunning total as the key + a hash map
O(1) extra space, order can changesort + two pointers

Common mistakes

MistakeWhat happensDo this instead
Inserting before checking (Two Sum)an element pairs with itselfcheck for the complement, then insert
Value as key with duplicates, no decisionthe wrong index is keptdecide: first, last, or a list
No seed entry for the empty prefixsubarrays starting at 0 are missed{0: 1} or {0: -1}
Mutating an object after using it as a keythe entry can't be found againimmutable keys (records, tuples)
A hash map when the values are small integersextra memory and boxingan array indexed by value

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Two Sum checks the map before inserting the current element. What breaks if you insert first?

Show answer

An element can pair with itself. If the target is exactly twice the element, the lookup finds the entry you just inserted, and the answer uses the same index twice.

2. The array has duplicate values, and you're building a value-to-index map. What decision do you have to make?

Show answer

Whether to keep the first index of a repeated value, the last one, or all of them. Each answers a different question. The problem decides which is right, not the map.

3. Counting subarrays by XOR stores a count per value. "The longest subarray summing to k" stored only the earliest index. Why the difference?

Show answer

They ask different things. The longest subarray needs the earliest match, because later matches only give shorter subarrays. "How many" needs every match, because each one is a separate subarray to count.

Key takeaways

  • Check the map for the complement, then insert the current element. That order stops an element pairing with itself.
  • With repeated values, decide what the map keeps: the first index, the last, or all of them.
  • Prefix XOR plus a map counts subarrays with a given XOR. XOR undoes itself the way subtraction undoes a sum.
  • Hashing spends O(n) memory for O(n) time. When memory matters more, sorting plus two pointers does the same job in O(n log n) time and O(1) space.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Seen before?a hash set
Index or count of what you've seena hash map
Counts of small integersan array
Subarrays with a given sum or XORrunning total as key, seeded
O(1) extra spacesort + two pointers

Snippets worth knowing by heart

Java

Map<Integer, Integer> indexOf = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    Integer j = indexOf.get(target - nums[i]);   // check first...
    if (j != null) return new int[] {j, i};
    indexOf.put(nums[i], i);                     // ...then insert
}

Python

index_of = {}
for i, x in enumerate(nums):
    if target - x in index_of:        # check first...
        return [index_of[target - x], i]
    index_of[x] = i                   # ...then insert

The traps

  • Insert before check: in Two Sum an element pairs with itself. Look up the complement first, then insert.
  • Duplicates with no decision: the map silently keeps the wrong index. Decide up front: first, last, or a list.
  • No seed for the empty prefix: subarrays starting at index 0 are missed. Seed the map with {0: 1} or {0: -1}.
  • Mutable keys: change an object after using it as a key and the entry is lost. Use records or tuples.
  • A map where an array would do: small integer keys pay for hashing and boxing. Index an array by value.

Staff-level points, one line each

  • O(1) is an average: 20,000 colliding Java keys were over 50x slower.
  • "Aa" and "BB" both hash to 2112; blocks of them give endless collisions.
  • Python salts str hashes per process (PYTHONHASHSEED); Java uses tree bins.
  • A mutated List key: get returned null, size still 1.
  • Python: {1, 1.0, True} is one key; hash(-1) is -2.
  • Java: Integer 1 and Long 1 are different keys.

Say it in an interview

  • "I trade O(n) memory for an O(1) lookup instead of a rescan."
  • "I check for the complement before inserting, so an element can't pair with itself."
  • "Keys are immutable, so their hash can't change while they're in the map."
  • "If memory mattered, I'd sort and use two pointers instead."

Practice

This skill comes with 11 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Array Hashing free

Two Sum

EasyFreeCoding problemJava · Python

Given an unsorted list of integers nums and a number target, find the two positions whose values add up to target. Each test has exactly one such pair, and a position can't be paired with itself. Return the two 0-based positions, the smaller one first.

Set Mismatch

EasyFreeCoding problemJava · Python

nums was meant to hold every number from 1 to n exactly once, where n is its length. A mistake crept in: one number was written twice, and so another number never made it in. Return [duplicate, missing].

Subarray with Given XOR

MediumProCoding problemJava · Python

Given an array nums and an integer k, count the subarrays whose elements XOR together to k.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Array Hashing free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime