Java 8 Collectors toMap

1. Overview

In this quick tutorial, we’re going to talk about the toMap() method of the Collectors class. We’ll use it to collect Streams into a Map instance.

For all the examples covered here, we’ll use a list of books as a starting point and transform it into different Map implementations.

2. List to Map

We’ll start with the simplest case, by transforming a List into a Map.

Here is how we define our Book class:

class Book {
    private String name;
    private int releaseYear;
    private String isbn;
    
    // getters and setters
}

And we’ll create a list of books to validate our code:

List<Book> bookList = new ArrayList<>();
bookList.add(new Book("The Fellowship of the Ring", 1954, "0395489318"));
bookList.add(new Book("The Two Towers", 1954, "0345339711"));
bookList.add(new Book("The Return of the King", 1955, "0618129111"));

For this scenario we’ll use the following overload of the toMap() method:

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper)

With toMap, we can indicate strategies for how to get the key and value for the map:

public Map<String, String> listToMap(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getIsbn, Book::getName));
}

And we can easily validate that it works:

@Test
public void whenConvertFromListToMap() {
    assertTrue(convertToMap.listToMap(bookList).size() == 3);
}

3. Solving Key Conflicts

The example above worked well, but what would happen with a duplicate key?

Let’s imagine that we keyed our Map by each Book‘s release year:

public Map<Integer, Book> listToMapWithDupKeyError(List<Book> books) {
    return books.stream().collect(
      Collectors.toMap(Book::getReleaseYear, Function.identity()));
}

Given our earlier list of books, we’d see an IllegalStateException:

@Test(expected = IllegalStateException.class)
public void whenMapHasDuplicateKey_without_merge_function_then_runtime_exception() {
    convertToMap.listToMapWithDupKeyError(bookList);
}

To resolve it, we need to use a different method with an additional parameter, the mergeFunction:

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper,
  BinaryOperator<U> mergeFunction)

Let’s introduce a merge function that indicates that, in the case of a collision, we keep the existing entry:

public Map<Integer, Book> listToMapWithDupKey(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
      (existing, replacement) -> existing));
}

Or in other words, we get first-win behavior:

@Test
public void whenMapHasDuplicateKeyThenMergeFunctionHandlesCollision() {
    Map<Integer, Book> booksByYear = convertToMap.listToMapWithDupKey(bookList);
    assertEquals(2, booksByYear.size());
    assertEquals("0395489318", booksByYear.get(1954).getIsbn());
}

4. Other Map Types

By default, a toMap() method will return a HashMap.

But we can return different Map implementations:

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  Function<? super T, ? extends U> valueMapper,
  BinaryOperator<U> mergeFunction,
  Supplier<M> mapSupplier)

where the mapSupplier is a function that returns a new, empty Map with the results.

4.1. List to ConcurrentMap

Let’s take the same example and add a mapSupplier function to return a ConcurrentHashMap:

public Map<Integer, Book> listToConcurrentMap(List<Book> books) {
    return books.stream().collect(Collectors.toMap(Book::getReleaseYear, Function.identity(),
      (o1, o2) -> o1, ConcurrentHashMap::new));
}

We’ll go on and test our code:

@Test
public void whenCreateConcurrentHashMap() {
    assertTrue(convertToMap.listToConcurrentMap(bookList) instanceof ConcurrentHashMap);
}

4.2. Sorted Map

Lastly, let’s see how to return a sorted map. For that, we’ll use a TreeMap as a mapSupplier parameter.

Because a TreeMap is sorted according to the natural ordering of its keys by default, we don’t have to explicitly sort the books ourselves:

public TreeMap<String, Book> listToSortedMap(List<Book> books) {
    return books.stream() 
      .collect(
        Collectors.toMap(Book::getName, Function.identity(), (o1, o2) -> o1, TreeMap::new));
}

So in our case, the returned TreeMap will be sorted in alphabetical order by the book name:

@Test
public void whenMapisSorted() {
    assertTrue(convertToMap.listToSortedMap(bookList).firstKey().equals(
      "The Fellowship of the Ring"));
}

5. Conclusion

In this article, we looked into the toMap() method of the Collectors class. It allows us to create a new Map from a Stream.

We also learned how to resolve key conflicts and create different map implementations.

As always, the code is available over on GitHub.

Related posts:

Display Auto-Configuration Report in Spring Boot
Marker Interface trong Java
Java Program to Implement Stack API
Receive email using POP3
Java Program to Implement Dijkstra’s Algorithm using Priority Queue
Extract network card address
HashSet trong Java hoạt động như thế nào?
Java Program to Implement Merge Sort Algorithm on Linked List
Logout in an OAuth Secured Application
Quick Guide to @RestClientTest in Spring Boot
Java Program to Implement Hash Tables Chaining with Doubly Linked Lists
Java Program to Implement LinkedHashMap API
Java Program to Implement AA Tree
Java equals() and hashCode() Contracts
Java Program to Compute DFT Coefficients Directly
Java Program to find the number of occurrences of a given number using Binary Search approach
Java Program to Implement Dijkstra’s Algorithm using Queue
Validations for Enum Types
Java Program to Implement Hash Tables Chaining with List Heads
Java Program to Find Shortest Path Between All Vertices Using Floyd-Warshall’s Algorithm
Java Program to Find Minimum Element in an Array using Linear Search
Java Program to Implement SimpeBindings API
Spring Boot: Customize Whitelabel Error Page
Redirect to Different Pages after Login with Spring Security
A Guide to Spring Cloud Netflix – Hystrix
Java – Write to File
Concurrent Test Execution in Spring 5
Java Program to Implement Karatsuba Multiplication Algorithm
Java Program to Implement Self organizing List
Java Program to Use Boruvka’s Algorithm to Find the Minimum Spanning Tree
Spring Boot - Admin Server
Dynamic Proxies in Java