Definition
- entrySet(): returns
Setwhere each entry holds both key and value as direct field references> - keySet()+get(): iterates keys, then for each key calls
get()which re-computes the hash, finds the bucket, and walks the chain - Performance: for n entries, entrySet does n accesses; keySet+get does 2n (n iterations + n hash lookups)
- forEach: Java 8's
map.forEach((k, v) -> ...)is equivalent to entrySet internally - Streams: always use
map.entrySet().stream()when you need both key and value in the pipeline
entrySet is like opening the filing cabinet and reading each folder directly – account number and balance in one pass. keySet+get is like writing down all account numbers first, then going back to the cabinet for each number, finding the drawer, finding the folder, and reading the balance. Double the work.
Understanding the Concepts
Why is entrySet() faster than keySet()+get()?
When you iterate with entrySet(), each Map.Entry object already holds references to both the key and the value. Reading them is a simple field access – O(1) with no computation.
When you iterate with keySet() and call get(key) for each key, HashMap must re-compute the hash, find the bucket, and walk the chain (comparing hash and equals for each node). This is a full hash lookup for every single entry.
package com.bank.demo;
import java.math.BigDecimal;
import java.util.*;
public class EntrySetVsKeySetDemo {
public static void main(String[] args) {
Map<String, BigDecimal> balances = new HashMap<>();
for (int i = 0; i < 200_000; i++) {
balances.put("ACC" + String.format("%06d", i), BigDecimal.valueOf(i * 100));
}
// FAST: entrySet – one pass, no re-lookup
long start = System.nanoTime();
BigDecimal sum1 = BigDecimal.ZERO;
for (Map.Entry<String, BigDecimal> entry : balances.entrySet()) {
String key = entry.getKey(); // O(1) field access
BigDecimal value = entry.getValue(); // O(1) field access
sum1 = sum1.add(value);
}
long entrySetTime = System.nanoTime() - start;
// SLOW: keySet + get – double lookup
start = System.nanoTime();
BigDecimal sum2 = BigDecimal.ZERO;
for (String key : balances.keySet()) {
BigDecimal value = balances.get(key); // hash + bucket + chain walk!
sum2 = sum2.add(value);
}
long keySetTime = System.nanoTime() - start;
System.out.printf("entrySet: %d ms%n", entrySetTime / 1_000_000);
System.out.printf("keySet+get: %d ms%n", keySetTime / 1_000_000);
System.out.printf("keySet+get is %.0f%% slower%n",
((double) keySetTime / entrySetTime - 1) * 100);
}
}
entrySet: 85 ms
keySet+get: 125 ms
keySet+get is 47% slower
What are the correct iteration patterns?
package com.bank.demo;
import java.math.BigDecimal;
import java.util.*;
public class IterationPatternsDemo {
public static void main(String[] args) {
Map<String, BigDecimal> balances = Map.of(
"SAV2001", new BigDecimal("150000"),
"CUR3001", new BigDecimal("75000"),
"FD4001", new BigDecimal("500000")
);
// Pattern 1: entrySet (pre-Java 8)
for (Map.Entry<String, BigDecimal> entry : balances.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Pattern 2: forEach (Java 8+, cleanest)
balances.forEach((account, balance) ->
System.out.println(account + ": " + balance));
// Pattern 3: stream with entrySet (for filtering/mapping)
balances.entrySet().stream()
.filter(e -> e.getValue().compareTo(new BigDecimal("100000")) > 0)
.forEach(e -> System.out.println("High balance: " + e.getKey()));
// Pattern 4: keySet only (when you only need keys)
for (String account : balances.keySet()) {
System.out.println("Account: " + account);
}
// Pattern 5: values only (when you only need values)
BigDecimal total = BigDecimal.ZERO;
for (BigDecimal balance : balances.values()) {
total = total.add(balance);
}
System.out.println("Total: " + total);
}
}
What are common mistakes?
Mistake: Using keySet+get when you need both key and value
// WRONG – redundant hash lookup for every entry
for (String key : map.keySet()) {
BigDecimal value = map.get(key); // unnecessary hash + chain walk
process(key, value);
}
// CORRECT – direct access via entrySet
for (var entry : map.entrySet()) {
process(entry.getKey(), entry.getValue());
}
// CLEANEST – Java 8 forEach
map.forEach((key, value) -> process(key, value));
What should a senior engineer know?
- The difference grows with hash complexity. If keys have expensive hashCode() methods (like long strings or composite keys), the penalty of re-hashing in keySet+get is even larger.
- Entry objects are not copies. The Map.Entry objects returned by entrySet() are backed by the map. Calling entry.setValue(newValue) modifies the map directly.
- entrySet, keySet, and values are views. They are not copies. Removing an element from any of these views removes it from the underlying map.
- SonarQube and IntelliJ flag this. Static analysis tools detect the keySet+get anti-pattern and suggest entrySet. This is a common code review comment.
What are common interview follow-up questions?
- "Why is keySet+get slower?" – Redundant hash computation and bucket traversal for each key. - "What is the cleanest way to iterate a map?" – Java 8's forEach((k, v) -> ...). - "Can you modify the map through entrySet?" – Yes, entry.setValue() modifies the map directly.
Visual Overview
entrySet() – one traversal: ┌────────────────────────────────┐ │ for (entry : entrySet()) │ │ entry.getKey() → O(1) ref │ │ entry.getValue() → O(1) ref │ │ Total: n iterations │ └────────────────────────────────┘ keySet() + get() – double work: ┌────────────────────────────────┐ │ for (key : keySet()) │ │ map.get(key) → │ │ 1. hash(key) → compute│ │ 2. find bucket → index │ │ 3. walk chain → equals │ │ Total: n iterations + n lookups│ └────────────────────────────────┘
Key Takeaway
When iterating a HashMap and you need both key and value, always use entrySet() or Java 8's forEach. The entrySet gives you Map.Entry objects with direct field access to both key and value – no recomputation needed. Using keySet+get forces a second hash lookup for every key, adding 30-50% overhead for large maps.
In the National Bank of India's EOD batch processing, Rajesh generates account statements by iterating the balance cache. Switching from keySet+get to forEach reduced the iteration time from 125 ms to 85 ms for 200K entries. For the 2 million account quarterly report, the savings were even more significant.
Remember one sentence: entrySet gives you key and value in one step; keySet+get re-hashes every key – always use entrySet when you need both.
---