Definition
- entrySet().stream() iterates over department-to-names entries.
- flatMap expands each department's list of names into individual name-department pairs.
- toMap(name, department) builds the inverted map keyed by name.
- If a name appears in two departments, the two-argument toMap throws; add a merge function or use groupingBy.
- Map.entry(key, value) creates immutable key-value pairs for the intermediate stream.
Priya at the National Bank of India has a chart listing who works in each department. She wants the opposite lookup: type a person's name and instantly see their department. So she flips the chart around, turning department-to-names into name-to-department.
Understanding the Concepts
How do you invert a one-to-many map?
The source map has departments as keys and lists of names as values. You stream entrySet, and for each entry you expand the name list while remembering the department. Then you collect the pairs into the inverted map.
package com.bank.hr;
import java.util.*;
import java.util.stream.Collectors;
public class InvertMap {
public static void main(String[] args) {
Map<String, List<String>> deptToNames = Map.of(
"Loans", List.of("Rajesh", "Amit"),
"IT", List.of("Priya", "Kavita"),
"Finance", List.of("Neha")
);
Map<String, String> nameToDept = deptToNames.entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.map(name -> Map.entry(name, entry.getKey())))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue));
System.out.println(nameToDept);
System.out.println("Priya works in: " + nameToDept.get("Priya"));
}
}
{Neha=Finance, Kavita=IT, Rajesh=Loans, Amit=Loans, Priya=IT}
Priya works in: IT
Why is flatMap needed here?
Each entry has a list of names. Without flatMap you would have a stream of lists. flatMap flattens those nested lists into a single stream of individual name-department pairs.
Before flatMap: Stream<Entry<String, List<String>>>
{Loans: [Rajesh, Amit]}, {IT: [Priya, Kavita]}
After flatMap: Stream<Entry<String, String>>
{Rajesh, Loans}, {Amit, Loans}, {Priya, IT}, {Kavita, IT}
What happens if a name appears in two departments?
The two-argument toMap throws IllegalStateException on a duplicate key.
Map<String, List<String>> withDuplicate = Map.of(
"Loans", List.of("Rajesh", "Amit"),
"IT", List.of("Amit", "Kavita") // Amit appears twice!
);
// WRONG: throws IllegalStateException for key "Amit"
Map<String, String> crash = withDuplicate.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(name -> Map.entry(name, e.getKey())))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue));
To handle duplicates, either supply a merge function or invert into a list:
// Option 1: merge function (keep the first department)
Map<String, String> keepFirst = withDuplicate.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(name -> Map.entry(name, e.getKey())))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(existing, incoming) -> existing));
// Option 2: invert into Map<String, List<String>>
Map<String, List<String>> nameToAllDepts = withDuplicate.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(name -> Map.entry(name, e.getKey())))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
System.out.println(nameToAllDepts);
// {Amit=[Loans, IT], Rajesh=[Loans], Kavita=[IT]}
What are common mistakes?
Mistake: forgetting to handle the uniqueness assumption.
// WRONG: assumes all names are unique across departments
// but does not document or enforce that assumption
Map<String, String> fragile = deptToNames.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(n -> Map.entry(n, e.getKey())))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue));
// CORRECT: make the assumption explicit with a merge function
Map<String, String> robust = deptToNames.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(n -> Map.entry(n, e.getKey())))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new IllegalStateException(
"Duplicate name: " + a + " vs " + b); }));
Can you invert a one-to-one map more simply?
If the source map is one-to-one (each value is a single string, not a list), no flatMap is needed.
Map<String, String> deptToManager = Map.of(
"Loans", "Rajesh", "IT", "Priya", "Finance", "Neha");
Map<String, String> managerToDept = deptToManager.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey));
System.out.println(managerToDept);
// {Rajesh=Loans, Priya=IT, Neha=Finance}
What should a senior engineer know?
- Inversion safety depends on value cardinality: a one-to-many map inverts to many-to-one only if the many side is unique.
- When uniqueness is not guaranteed, groupingBy is the safer inverse, producing Map
>. - Map.entry() creates an immutable entry; AbstractMap.SimpleEntry is the mutable alternative if you need to modify it.
- Being explicit about the uniqueness assumption is what separates a robust inversion from a fragile one.
What are common interview follow-up questions?
- "How do you invert a Map
>?" Same pattern, just stream the set instead of the list. - "What if the original map has null values?" Filter out null entries before flatMapping.
- "Can you invert a Map
where values repeat?" Use groupingBy to collect keys sharing a value into a list.
Visual Overview
Source: Map<String, List<String>>
┌─────────────────────────────────┐
│ Loans → [Rajesh, Amit] │
│ IT → [Priya, Kavita] │
│ Finance → [Neha] │
└─────────────────────────────────┘
│
│ flatMap: expand each dept's names
▼
Stream of pairs:
(Rajesh, Loans), (Amit, Loans),
(Priya, IT), (Kavita, IT), (Neha, Finance)
│
│ toMap(name, dept)
▼
Inverted: Map<String, String>
┌──────────────────────────────┐
│ Rajesh → Loans │
│ Amit → Loans │
│ Priya → IT │
│ Kavita → IT │
│ Neha → Finance │
└──────────────────────────────┘
Key Takeaway
Inverting a one-to-many map means taking a map of department-to-names and producing the reverse lookup of name-to-department. Stream the entrySet, and for each entry use flatMap to expand that department's list of names into a stream of name-department pairs. Then collect with toMap keyed by name.
flatMap is the key operation: it flattens nested lists into a single stream of pairs that toMap can rekey. At the National Bank of India, the roster was stored as department-to-names, but the search screen needed the opposite lookup.
The critical point is the uniqueness assumption: this works cleanly only when every name is unique across departments. If a name appears in two departments, toMap throws. Either add a merge function to pick a winner, or invert into Map
Remember one sentence: invert a one-to-many map by flatMapping the lists into name-department pairs and collecting with toMap, and always decide how to handle names appearing in multiple groups.
---