Back to Stream: map & Transform
Q27Question 27 of 34

Invert a Map<String, List<String>> (department to names) into Map<String, String> (name to department)

Key Concepts
  • Inverting a one-to-many map means flattening each department's list and mapping every name back to its department
  • Stream entrySet, flatMap to expand the lists into name-department pairs, then collect with toMap
  • flatMap is the key operation: it turns nested lists into a single flat stream of pairs
  • If a name appears in two departments, toMap throws unless you supply a merge function
  • Use Map.entry(name, dept) or AbstractMap.SimpleEntry to create intermediate key-value pairs
  • For names appearing in multiple departments, invert into Map<String, List<String>> using groupingBy instead

Definition

Stream entries, flatMap the name lists into name-department pairs, and toMap(name, dept) to invert the map.
  • 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.

JAVA
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"));
    }
}
TEXT
{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.

TEXT
  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.

JAVA
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:

JAVA
// 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.

JAVA
// 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.

JAVA
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

DIAGRAM
  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> using groupingBy.

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.

---

Share
Use ← → arrow keys to navigate