Working With Maps Using Streams

1. Introduction

In this tutorial, we’ll discuss some examples of how to use Java Streamsto work with Maps. It’s worth noting that some of these exercises could be solved using a bidirectional Map data structure, but we’re interested here in a functional approach.

First, we’ll explain the basic idea we’ll be using to work with Maps and Streams. Then we’ll present a couple of different problems related to Maps and their concrete solutions using Streams.

2. Basic Idea

The principal thing to notice is that Streams are sequences of elements which can be easily obtained from a Collection.

Maps have a different structure, with a mapping from keys to values, without sequence. However, this doesn’t mean that we can’t convert a Map structure into different sequences which then allow us to work in a natural way with the Stream API.

Let’s see ways of obtaining different Collections from a Map, which we can then pivot into a Stream:

Map<String, Integer> someMap = new HashMap<>();

We can obtain a set of key-value pairs:

Set<Map.Entry<String, Integer>> entries = someMap.entrySet();

We can also get the key set associated with the Map:

Set<String> keySet = someMap.keySet();

Or we could work directly with the set of values:

Collection<Integer> values = someMap.values();

These each give us an entry point to process those collections by obtaining streams from them:

Stream<Map.Entry<String, Integer>> entriesStream = entries.stream();
Stream<Integer> valuesStream = values.stream();
Stream<String> keysStream = keySet.stream();

3. Getting a Map‘s Keys Using Streams

3.1. Input Data

Let’s assume we have a Map:

Map<String, String> books = new HashMap<>();
books.put(
"978-0201633610", "Design patterns : elements of reusable object-oriented software");
books.put(
  "978-1617291999", "Java 8 in Action: Lambdas, Streams, and functional-style programming");
books.put("978-0134685991", "Effective Java");

We are interested in finding the ISBN for the book titled “Effective Java.”

3.2. Retrieving a Match

Since the book title could not exist in our Map, we want to be able to indicate that there is no associated ISBN for it. We can use an Optional to express that:

Let’s assume for this example that we are interested in any key for a book matching that title:

Optional<String> optionalIsbn = books.entrySet().stream()
  .filter(e -> "Effective Java".equals(e.getValue()))
  .map(Map.Entry::getKey)
  .findFirst();

assertEquals("978-0134685991", optionalIsbn.get());

Let’s analyze the code. First, we obtain the entrySet from the Map, as we saw previously.

We only want to consider the entries with “Effective Java” as the title, so the first intermediate operation will be a filter.

We’re not interested in the whole Map entry, but in the key of each entry. So the next chained intermediate operation does just that: it is a map operation that will generate a new stream as output, which will contain only the keys for the entries that matched the title we were looking for.

As we only want one result, we can apply the findFirst() terminal operation, which will provide the initial value in the Stream as an Optional object.

Let’s see a case in which a title does not exist:

Optional<String> optionalIsbn = books.entrySet().stream()
  .filter(e -> "Non Existent Title".equals(e.getValue()))
  .map(Map.Entry::getKey).findFirst();

assertEquals(false, optionalIsbn.isPresent());

3.3. Retrieving Multiple Results

Now let’s change the problem to see how we could deal with returning multiple results instead of one.

To have multiple results returned, let’s add the following book to our Map:

books.put("978-0321356680", "Effective Java: Second Edition");

So now if we look for all books that start with “Effective Java,” we’ll get more than one result back:

List<String> isbnCodes = books.entrySet().stream()
  .filter(e -> e.getValue().startsWith("Effective Java"))
  .map(Map.Entry::getKey)
  .collect(Collectors.toList());

assertTrue(isbnCodes.contains("978-0321356680"));
assertTrue(isbnCodes.contains("978-0134685991"));

What we have done in this case is to replace the filter condition to verify if the value in the Map starts with “Effective Java” instead of comparing for String equality.

This time we collect the results, instead of just picking the first, and put the matches into a List.

4. Getting a Map‘s Values Using Streams

Now let’s focus on a different problem with maps. Instead of obtaining ISBNs based on the titles, we’ll try and get titles based on the ISBNs.

Let’s use the original Map. We want to find titles with an ISBN starting with “978-0”.

List<String> titles = books.entrySet().stream()
  .filter(e -> e.getKey().startsWith("978-0"))
  .map(Map.Entry::getValue)
  .collect(Collectors.toList());

assertEquals(2, titles.size());
assertTrue(titles.contains(
  "Design patterns : elements of reusable object-oriented software"));
assertTrue(titles.contains("Effective Java"));

This solution is similar to the solutions of our previous set of problems; we stream the entry set, and then filter, map, and collect.

Also like before, if we wanted to return only the first match, then after the map method we could call the findFirst() method instead of collecting all the results in a List.

5. Conclusion

In this article, we’ve demonstrated how to process a Map in a functional way.

In particular, we have seen that once we switch to using the associated collections to Maps, processing using Streams becomes much easier and intuitive.

Of course, all of the examples in this article can be found in the GitHub project.

Related posts:

Spring Web Annotations
Setting a Request Timeout for a Spring REST API
Functional Interfaces in Java 8
Xây dựng ứng dụng Client-Server với Socket trong Java
Introduction to the Functional Web Framework in Spring 5
Java Program to Implement Cubic convergence 1/pi Algorithm
Java Program to Implement Weight Balanced Tree
Introduction to the Java NIO2 File API
Java InputStream to String
What is Thread-Safety and How to Achieve it?
Java Program to Solve the Fractional Knapsack Problem
Java Program to Implement Fibonacci Heap
Java Program to Represent Graph Using Linked List
Java Program to Use Above Below Primitive to Test Whether Two Lines Intersect
Java Program to Implement Horner Algorithm
Java Program to Implement the Schonhage-Strassen Algorithm for Multiplication of Two Numbers
Java Program to Check Whether an Input Binary Tree is the Sub Tree of the Binary Tree
Quick Intro to Spring Cloud Configuration
Spring Cloud – Bootstrapping
Java Program to Find Minimum Number of Edges to Cut to make the Graph Disconnected
Using a Spring Cloud App Starter
How to use the Spring FactoryBean?
Giới thiệu Swagger – Công cụ document cho RESTfull APIs
Java Program to Perform Complex Number Multiplication
Java Program to Implement Sorting of Less than 100 Numbers in O(n) Complexity
The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5
Spring Boot - Building RESTful Web Services
Registration with Spring Security – Password Encoding
Deque và ArrayDeque trong Java
Giới thiệu JDBC Connection Pool
Java 14 Record Keyword
Java Program to Find Median of Elements where Elements are Stored in 2 Different Arrays