How to Filter a Map by Key or Value in Java

How to Filter a Map by Key or Value in Java

Introduction

Map implementations in Java represent structures that map keys to values. A Map cannot contain duplicate keys and each can at most be mapped to one value. The Map<K,V> implementations are generic and accept any K (key) and V (value) to be mapped.

The Map interface also includes methods for some basic operations (such as put(), get(), containsKey(), containsValue(), size(), etc.), bulk operations (such as putAll() and clear()) and collection views (such as keySet(), entrySet() and values()).

The most prominent Map implementations used for general purposes are: HashMap, TreeMap and LinkedHashMap.

In this article, we'll take a look at how to filter a Map by its keys and values:

Filter a Map with enhanced for-loops

Let's populate a HashMap with some key-value pairs:

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

employeeMap.put(35, "Mark");
employeeMap.put(40, "John");
employeeMap.put(23, "Michael");
employeeMap.put(31, "Jim");
employeeMap.put(25, "Kevin");

The Map has keys of type Integer and values of type String. They represent the age and name of the employees.

We'll filter this map by keys and values and save the results in a Collection, such as another Map implementation, or even another HashMap.

Let's go with the LinkedHashMap which preserves the order of insertion:

Map<Integer, String> linkedHashMap = new LinkedHashMap<>();

for (Map.Entry<Integer, String> employee : employeeMap.entrySet()) {
    if(employee.getKey() > 30){
        linkedHashMap.put(employee.getKey(), employee.getValue());
    }
}
    
System.out.println("Filtered Map: " + linkedHashMap);

Here, we've gone through the entrySet() of the employeeMap, and added each employee into a LinkedHashMap via its put() method. This would work the exact same for the HashMap implementation, but it wouldn't preserve the order of insertion:

Filtered Map: {35=Mark, 40=John, 31=Jim}

Filtering out by values boils down to much the same approach, albeit, we'll be checking the value of each entry and using that in a condition:

Map<Integer, String> linkedHashMap = new LinkedHashMap<>();

for (Map.Entry<Integer, String> employee : employeeMap.entrySet()) {
    if(employee.getValue().equals("Mark")){
        linkedHashMap.put(employee.getKey(), employee.getValue());
    }
}

System.out.println("Filtered Map: " + linkedHashMap);

And this would output:

Filtered Map: {35=Mark}

This is the manual way to filter a map - iterating and picking the desired elements. Let's now take a look at a more readable, and friendlier way - via the Stream API.

Stream.filter()

A more modern way to filter maps would be leveraging the Stream API from Java 8, which makes this process much more readable. The filter() method of the Stream class, as the name suggests, filters any Collection based on a given condition.

For example, given a Collection of names, you can filter them out based on conditions such as - containing certain characters or starting with a specific character.

Filter a Map by Keys with Stream.filter()

Let's leverage the Stream API to filter out this same map given the same condition. We'll stream() the entrySet() of the map, and collect() it back into a Map:

Map<Integer, String> filteredMap = employeeMap.entrySet()
        .stream().filter(x->x.getKey() > 30)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

System.out.println("Filtered map: " + filteredMap);

What this code does is much the same as what we did manually - for each element in the map's set, we check if their key's value is greater than 30 and collect the values into a new Map, with their respective keys and values supplied through the getKey() and getValue() method references:

Filtered map: {35=Mark, 40=John, 31=Jim}

Filter a Map by Values with Stream.filter()

Now, let's populate another map, and instead of a <Integer, String> key-value pair, we'll use a <String, String> pair:

Map<String, String> cityMap = new HashMap<>();

cityMap.put("Tokyo", "Japan");
cityMap.put("Berlin", "Germany");
cityMap.put("Kyoto", "Japan");
cityMap.put("Belgrade", "Serbia");
cityMap.put("Madrid", "Spain");

This time around, we've got city-country pairs, where keys are individual cities, and the values are the countries they're located in. Values don't have to be unique. Kyoto and Tokyo, which are both unique keys can have the same value - Japan.

Sorting this map by values, again, boils down to much the same approach as before - we'll simply use the value, via the getValue() method in the filtering condition:

Map<String, String> filteredMap = citiesMap.entrySet()
        .stream().filter(x->"Japan".equals(x.getValue()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

System.out.println("Filtered map: " + filteredMap)

Now, this results in a filtered map that contains both Tokyo and Kyoto:

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Filtered map: {Tokyo=Japan, Kyoto=Japan}

You can get creative with the outputs and results here. For example, instead of putting these elements into a new map and returning that - we can manipulate the resulting value into other structures as well. For example, we could filter out the keys what have Japan and Serbia as values, and join the keys into a single String:

String filteredMap = citiesMap.entrySet()
			.stream().filter(x-> x.getValue().equals("Japan") || 
                                 x.getValue().equals("Serbia"))
            .map(Map.Entry::getKey).collect(Collectors.joining(", "));

System.out.println("Filtered map: " + filteredMap);

Here, we've used a different Collector than before. The Collectors.joining() returns a new Collector that joins the elements into a String. Other than the CharSequence delimiter we've passed in, we could've also supplied a CharSequence prefix and CharSequence suffix for each joined element.

This results in a String, with all of the filtered elements, separated by a ,:

Filtered map: Belgrade, Tokyo, Kyoto

Conclusion

In this article, we've taken a look at how to filter a Map in Java. We've first gone over how to use enhanced for-loops for pre-Java 8 projects, after which we've dived into the Steam API and leveraged the filter() method.

Filtering maps by either values or keys is rendered into a simple, one-liner task with the help of the Stream API, and you have a wide variety of Collectors to format the output to your liking.

Last Updated: April 9th, 2021
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Want a remote job?

    Prepping for an interview?

    • Improve your skills by solving one coding problem every day
    • Get the solutions the next morning via email
    • Practice on actual problems asked by top companies, like:
     
     
     

    Better understand your data with visualizations

    With over 330+ pages, you'll learn the ins and outs of visualizing data in Python with popular libraries like Matplotlib, Seaborn, Bokeh, and more.

    © 2013-2021 Stack Abuse. All rights reserved.