Wednesday, April 27, 2022

How to use filter + map + collect + Stream in Java? Example Tutorial

Hello guys, many of my readers emailed me to write a post about the map and filter function of Java 8 because they found it difficult to understand and use. Even though I have previously blogged about both map() and filter(), I am writing this post again to explain the concept in more layman's language for a better understanding of my readers and fellow Java developers. The map() function is a method in Stream class that represents a functional programming concept. In simple words, the map() is used to transform one object into another by applying a function. That's the reason the Stream.map(Function mapper) takes a function as an argument. For example, by using map() function, you can convert a list of String into List of Integer by applying Integer.valueOf() method to each String on the input list.

All you need is a mapping function to convert one object to another, and the map() function will do the transformation for you.

It is also an intermediate stream operation which means you can call other Stream methods like a filter or collect on this to create a chain of transformation.

Now, coming to the filter method, as its name suggests, it filters elements based upon a condition you gave it to you. For example, if your list contains numbers and you only want even numbers, then you can use the filter method to the only select number which is fully divisible by two.

The filter method essentially selects elements based upon the condition you provide. That's the reason that filter(Predicate condition) accepts a Predicate object which provides a function to apply a condition. If the condition evaluates true, then the object is selected; otherwise, it is ignored.

Similar to a map, the filter is also an intermediate operation which means you can call other Stream methods after calling filter.

The filter() method is also lazy, which means it will not be evaluated until you call a reduction method like collect, and it will stop as soon as it reaches the target.  If you are not familiar with Stream behavior, I suggest you check to Learn Java Functional Programming with Lambdas & Streams by Rang Rao Karnam on Udemy, which explains Stream fundamentals in good detail.





1. How to use map and filter in Java 8

You need an excellent example to understand a new concept, and that's why you are reading this article. Since String and Integer are the most common data type in Java, I have chosen an example which is both simple and exciting.

I have a list of String which is numbers, e.g. {"1", "2", "3", "4", "5", "6"} I want to process this list and need another List of Integer with just even numbers.

In order to find the even number I first need to convert a List of String to List of Integer and for that, I can use the map() method of java.util.Stream class, but before that we need a Stream as a map() is defined in java.util.stream. Stream class.

But, that's not difficult at all, as you can get the stream from any Collection, e.g. List or Set by calling the stream() method because it defined in java.util.Collection interface.

The map(Function mapper) method takes a Function, technically speaking an object of java.util.function.Function interface. This function is then applied to each element of Stream to convert into a type you want (see Collections to Stream on Pluralsight for more details)

Since, we need to convert an String to Integer, we can pass either Integer.parseInt() or Integer.valueOf() method to map() function. I have chose valueOf() method because of the reasons I mentioned in parseInt vs valueOf article i.e. performance and caching.

The map() will then return a Stream of Integer which contains both even and odd number. To select just even numbers, we can use the filter() method. It takes a Predicate object which is technically a function to convert an object to boolean. I mean, we pass an object, and it will return true or false. The filter uses that information to include the object in the result stream.

So, to include only even numbers we call filter( number -> number%2==0) which means each number will be divided by two and if there is no remainder, then it will be selected.

We are almost done, but so far we only have Stream of even integers not the List of even Integers, and that's why we need to use the collect() method, which collects elements form Stream into a specified Collection.

Since we need List, I called collect(Collectors.toList()) which will accumulate all even numbers into a List and return. Now you may be thinking how does it know to return List of Integer, well it get that information by Type inference because we have already specified that by storing the result into a List<Integer>.

If you want to know more about type inference in a lambda expression, The Complete Java MasterClass is an excellent place to start with.


2. Java 8 Map + Filter + Collect Example



2. Java 8 Map + Filter + Collect Example

Here is the Java program to implement whatever I have said in the above section. You can run this program in IDE or from the command line and see the result. You can also experiment with using more map() function or more filter() calls to make the composition longer and more sophisticated. You can even play with the collect() method to collect the result in a list, set, map, or any other collection.

package tool;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 
 * A simple Java Program to demonstrate how to use map and filter method Java 8.
 * In this program, we'll convert a list of String into a list of Integer and
 * then filter all even numbers.
 */
public class Hello {

  public static void main(String[] args) {

    List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");
    System.out.println("original list: " + numbers);

    List<Integer> even = numbers.stream()
                                .map(s -> Integer.valueOf(s))
                                .filter(number -> number % 2 == 0)
                                .collect(Collectors.toList());

    System.out.println("processed list, only even numbers: " + even);

  }

}

Output
original list: [1, 2, 3, 4, 5, 6]
the processed list, only even numbers: [2, 4, 6]


You can see that the original list contains numbers from 1 to 6 and the filtered list only contains even numbers, i.e. 2, 4, and 6.


The most important code in this example is the following 4 lines of Stream processing code:

Java 8 filter + map + collect + Stream example

This code is first doing a map and then filter and finally collect. You may be wondering whether the order will matter or not, well it does. Since our filter condition requires an int variable, we first need to convert Stream of String to Stream of Integer. That's why we called the map() function first.

Once we got the Stream of Integer, we can apply maths to find out even numbers, and we passed that condition to the filter method.

If we needed to filter on String, like select all string which has length > 2 then we would have called filter before map.

That's all about how to use map and filter in Java 8. We have seen an interesting example of how we can use the map to transform an object to another and filter to select an object-based upon condition. We have also learned how to compose operations on stream to write code which is both clear and concise.

Further Learning
The Complete Java MasterClass
From Collections to Streams in Java 8 Using Lambda Expressions
Java SE 8 for Programmers (book)
Refactoring to Java 8 Streams and Lambdas Self- Study Workshop


Other Java tutorials you may like
If you are interested in learning more about new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8:
  • The 2020 Java Developer RoadMap (see)
  • How to sort the may by values in Java 8? (example)
  • Difference between map() and flatMap in Java 8 (answer)
  • How to use Stream class in Java 8 (tutorial)
  • 10 Courses to learn Java in-depth (courses)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • 5 Books to Learn Java 8 from Scratch (books)
  • What is the default method in Java 8? (example)
  • How to join String in Java 8 (example)
  • Difference between abstract class and interface in Java 8? (answer)
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • How to sort the map by keys in Java 8? (example)
  • 15 Java Stream and Functional Programming interview questions (list)
  • How to convert List to Map in Java 8 (solution)
  • 10 examples of Optionals in Java 8? (example)

Thanks for reading this tutorial so far. If you like this Java 8 map + filter example and my explanation, then please share it with your friends and colleagues. If you have any questions or feedback, then please drop a note.

P. S. - If you are keen to learn Functional Programming in Java, particularly the Stream API and its functional methods but looking for a free online training course to start with then you can also check out this Free Java Functional Programming and Lambda course on Udemy. It's completely free and also covers new features from Java 9, 10, 11, 12, and 13.

5 comments:

  1. "divided by zero" to be replace by "divided by 2"

    ReplyDelete
  2. Thanks @Anonymous, corrected now.

    ReplyDelete
  3. Why do you need map here. without map it works same.

    ReplyDelete
  4. How can we achive the below one using functional interface.

    String id = "131sw21";
    Double totalSenders = 0.0;
    Double totalRecivers = 0.0;

    for(Record record : records){
    if(id.equals(record.getSenderId()){
    totalSenders += record.getCount().doubleValue;
    }else{
    totalRecivers += record.getCount().doubleValue;
    }
    }

    DTO

    class Record{

    private String id;
    private BigDecimal count;
    private String senderId;

    //setters
    //getters
    }


    ReplyDelete
  5. try something like this

    records.filter(record - id.equals(record.getSenderId()).mapToDouble(total - total+record.getCount());

    ReplyDelete