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:

Java Program to Perform Right Rotation on a Binary Search Tree
Lập trình hướng đối tượng (OOPs) trong java
Sử dụng JDBC API thực thi câu lệnh truy vấn dữ liệu
Java Program to Implement the Program Used in grep/egrep/fgrep
Java Program to implement Sparse Vector
Java Program to Implement Naor-Reingold Pseudo Random Function
List Interface trong Java
Java Program to Implement HashSet API
RegEx for matching Date Pattern in Java
Java InputStream to String
Java – InputStream to Reader
Java Program to Check Whether a Directed Graph Contains a Eulerian Cycle
Java Program to Test Using DFS Whether a Directed Graph is Strongly Connected or Not
Introduction to Spring Data MongoDB
Java Program to Give an Implementation of the Traditional Chinese Postman Problem
Java – Write an InputStream to a File
Java Program to Implement ConcurrentLinkedQueue API
Java Program to Implement Quick Sort Using Randomization
Life Cycle of a Thread in Java
Spring Cloud AWS – EC2
The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5
Java Program to Check Whether a Directed Graph Contains a Eulerian Path
Loại bỏ các phần tử trùng trong một ArrayList như thế nào?
SOAP Web service: Authentication trong JAX-WS
Hướng dẫn Java Design Pattern – Adapter
Notify User of Login From New Device or Location
Upload and Display Excel Files with Spring MVC
Java Program to Describe the Representation of Graph using Adjacency List
Java Program to Implement Disjoint Set Data Structure
Hướng dẫn Java Design Pattern – Abstract Factory
A Guide to EnumMap
Java Program to Implement Sieve Of Sundaram