September 18, 2021

flatMap() in Java Stream

When you use map operations in Java Stream resulting stream is obtained by applying the given function to all the elements of this stream. Java Stream API also provides a flatMap() method that apart from applying the given function to all the elements of this stream, flattens the resulting elements too so that all the nested elements are at the same level.

flatMap is an intermediate operation.

flatMap method signature

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

Here mapper is a non-interfering, stateless function applied to each element of the stream.

R is the element type of the new stream.

flatMap Java examples

Let’s try to understand with few examples how flatMap() flattens the structure and how it helps.

Suppose there is an ArrayList that contains ArrayLists in turn and you want to count the total number of elements in the list. If you use map function then you will get the number of elements as 2 because map function will get each nested list as element of the stream.

public class FlatMapExample {
  public static void main(String[] args) {
    List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
    long noOfElements = list.stream().map(a -> a.stream()).count();
    System.out.println("Number of elements- "+ noOfElements);
  }
}
Output
Number of elements- 2

When you use flatMap function, structure is flattened so that the number of elements is counted properly.

public class FlatMapExample {

  public static void main(String[] args) {
    List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
    long noOfElements = list.stream().flatMap(a -> a.stream()).count();
    System.out.println("Number of elements- "+ noOfElements);
  }
}
Output
Number of elements- 4

If you want to display elements in nested ArrayLists in uppercase using map function returns List<Stream<String>>

public class FlatMapExample {
  public static void main(String[] args) {
    List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
    List<Stream<String>> resultlist = list.stream()
                              .map(a -> a.stream()
                              .map(String::toUpperCase))
                              .collect(Collectors.toList());
    resultlist.forEach(a -> a.forEach(System.out::println));
  }
}

If you use flatMap in such scenario both lists are flattened so the return value is List

public class FlatMapExample {
  public static void main(String[] args) {
    List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
    List<String> resultlist = list.stream()
                        .flatMap(a -> a.stream()
                        .map(String::toUpperCase))
                        .collect(Collectors.toList());
    resultlist.forEach(System.out::println);
  }
}
Output
A
B
C
D

Here is another example where we have a List of lists. There is a class Order which has a field items of type List. Now you want to display all the items in all the orders.

public class Order {
  private String orderId;
  private List<String> items;
  public String getOrderId() {
    return orderId;
  }
  public void setOrderId(String orderId) {
    this.orderId = orderId;
  }
  public List<String> getItems() {
    return items;
  }
  public void setItems(List<String> items) {
    this.items = items;
  }
}
public class FlatMapExample {
  public static void main(String[] args) {
    // Create list of orders
    List<Order> listOfOrders = new ArrayList<Order>();
    Order order = new Order();
    order.setOrderId("1");
    order.setItems(Arrays.asList("Book", "Shoes", "Watch"));
    listOfOrders.add(order);
    order = new Order();
    order.setOrderId("2");
    order.setItems(Arrays.asList("Mobile", "Book"));
    listOfOrders.add(order);

    List<String> listOfItems = listOfOrders.stream()
                          .flatMap(o -> o.getItems()
                          .stream())
                          .collect(Collectors.toList());
    listOfItems.forEach(System.out::println);
  }
}
Output
Book
Shoes
Watch
Mobile
Book

flatMap for primitive type stream

There are also flatMap() variants to be used for getting primitive type streams in Java.

  • flatMapToInt(Function<? super T,? extends IntStream> mapper)- Returns an IntStream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
  • flatMapToLong(Function<? super T,? extends LongStream> mapper)- Returns a LongStream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
  • flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)- Returns a DoubleStream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.

flatMapToInt Java example

Getting an IntStream by flattening a two dimensional array.

int[][] numbers = {{7,8}, {4,5}, {3,4}};
IntStream iStream = Stream.of(numbers).flatMapToInt(n -> Arrays.stream(n));
iStream.forEach(System.out::println);
Output
7
8
4
5
3
4

That's all for the topic flatMap() in Java Stream. If something is missing or you have something to share about the topic please write a comment.


You may also like

September 17, 2021

map() Function in Java With Examples

In this post we’ll see examples of map() function in Java Stream API that is used to apply some transformation to the elements in a stream. When you use a map operation a new stream is returned consisting of the resultant elements after applying the given function to all the elements of source stream.

Generalized map() function in java.util.stream.Stream interface is-

<R> Stream<R> map(Function<? super T,? extends R> mapper)

Here R is the element type of the new interface.

mapper is a non-interfering, stateless function applied to each element, mapper is of type Function which is a functional interface and can be implemented as a lambda expression.

Apart from the generalized map() function there are also methods mapToInt(), mapToLong(), and mapToDouble() returning IntStream, LongStream and DoubleStream respectively which are specialized primitive type streams for these primitive data types.

In the primitive type streams there is also a mapToObj() method which Returns an object-valued Stream.

map() Java Stream examples

1- Converting each element in a Stream in upper case and collecting those elements in a List. For this requirement map() method can be used to apply upperCase functionality to all the elements of the stream and then collect the result of this transformation into a List using collect() method.

List<String> names = Stream.of("Jack", "Lisa", "Scott", "Nikita", "Tony")
			   .map(s -> s.toUpperCase())
			   .collect(Collectors.toList());	
names.forEach(System.out::println);
Output
JACK
LISA
SCOTT
NIKITA
TONY

2- Using map() method to get a new Stream having only the selected fields from the source stream. That way you can transform the stream to have elements of new type.

Let’s say there is an Employee class with name, dept, age fields and the source stream contains objects of type Employee. Requirement is to map name and dept fields to a new stream of EmpDept type.

class Employee {
  private String name;
  private String dept;
  private int salary;

  Employee(String name, String dept, int salary){
    this.name = name;
    this.dept = dept;
    this.salary = salary;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getSalary() {
    return salary;
  }
  public void setSalary(int salary) {
    this.salary = salary;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }
}

class EmpDept {
  private String name;
  private String dept;
  EmpDept(String name, String dept){
    this.name = name;
    this.dept = dept;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }
}

public class EmpStream {
  public static void main(String[] args) {
    List<Employee> employeeList = new ArrayList<>(); 

    employeeList.add(new Employee("Jack", "Finance", 5500)); 
    employeeList.add(new Employee("Lisa", "Finance", 5600)); 
    employeeList.add(new Employee("Scott", "Finance", 7000));
    employeeList.add(new Employee("Nikita", "IT", 4500));
    employeeList.add(new Employee("Tony", "IT", 8000)); 

    List<EmpDept> emp = employeeList.stream()
             .map(e -> new EmpDept(e.getName(), e.getDept()))
             .collect(Collectors.toList());
    emp.forEach(e -> System.out.println("Name- " + e.getName() + 
                        " Department- " + e.getDept()));
  }
}
Output
Name- Jack Department- Finance
Name- Lisa Department- Finance
Name- Scott Department- Finance
Name- Nikita Department- IT
Name- Tony Department- IT

3- map() with filter example- Using map method to get a new stream having employee names in finance department. Filter method is used to filter those employees not matching the given predicate.

List<String> emp = employeeList.stream()
				.filter(e -> e.getDept().equals("Finance"))
				.map(e -> e.getName())
				.collect(Collectors.toList());
							   
emp.forEach(System.out::println);
Output
Jack
Lisa
Scott

Java mapToInt() example

1- If you want to get the average of salaries for the employees, using mapToInt() method you can get an IntStream consisting of salaries and then apply average() method on that int stream.

List<Employee> employeeList = new ArrayList<>(); 

employeeList.add(new Employee("Jack", "Finance", 5500)); 
employeeList.add(new Employee("Lisa", "Finance", 5600)); 
employeeList.add(new Employee("Scott", "Finance", 7000));
employeeList.add(new Employee("Nikita", "IT", 4500));
employeeList.add(new Employee("Tony", "IT", 8000)); 

double avgSalary = employeeList.stream()
                               .mapToInt(e -> e.getSalary())
                               .average()
                               .getAsDouble();
							   
System.out.println("Average salary- " + avgSalary);
Output
Average salary- 6120.0

2- If you want to get the maximum salary, using mapToInt() method you can get an IntStream consisting of salaries and then apply max() method on that int stream.

List<Employee> employeeList = new ArrayList<>(); 

employeeList.add(new Employee("Jack", "Finance", 5500)); 
employeeList.add(new Employee("Lisa", "Finance", 5600)); 
employeeList.add(new Employee("Scott", "Finance", 7000));
employeeList.add(new Employee("Nikita", "IT", 4500));
employeeList.add(new Employee("Tony", "IT", 8000)); 

int maxSalary = employeeList.stream()
                            .mapToInt(e -> e.getSalary())
                            .max()
                            .getAsInt();
							   
System.out.println("Maximum salary- " + maxSalary);
Output
Maximum salary- 8000

That's all for the topic map() Function in Java With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

September 16, 2021

Java Stream Collectors.teeing() Examples

In this tutorial we’ll see how to use Collectors.teeing() method which is added in Java 12 to the Collectors class in the Java Stream API.

Collectors.teeing() method

The teeing() method let you create a composite of two Collectors and there is also a third argument; a merging function. Every element passed to the method is processed by both downstream collectors, then their results are merged using the specified merge function into the final result.

Method syntax

public static <T,R1,R2,R> Collector<T,?,R> teeing(Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)

Here parameters are-

  • downstream1- the first downstream collector
  • downstream2- the second downstream collector
  • merger- the function which merges two results into the single one

Collectors.teeing() Java examples

1. Getting count and sum of elements in a List using Collectors.teeing function. By passing Collectors.counting() and Collectors.summingInt() as two downstream Collectors you can do the job of both counting the number of elements and getting the sum of elements in a single operation. Merging operation does the job of storing both sum and count in a List and returning that List.

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

public class TeeingDemo {

  public static void main(String[] args) {
     List<Integer> listOfNumbers = Arrays.asList(10, 25, 9, 87, 56);
     List<Integer> list = listOfNumbers.stream()
                          .collect(Collectors.teeing(
                             Collectors.counting(), 
                             Collectors.summingInt(n -> n), 
                             (count, sum) -> {
                                List<Integer> l = new ArrayList<>();
                                l.add(count.intValue());
                                l.add(sum);
                                return l;
                              }));
     System.out.println("Number of elements in the list- " + list.get(0));
     System.out.println("Sum of elements in the list- " + list.get(1));
  }
}
Output
Number of elements in the list- 5
Sum of elements in the list- 187

2. Getting average of elements in a List. Here with in the teeing method first Collector does the job of counting elements, second Collector does the job of getting the sum of elements and the merger operation does the job of calculating average.

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

public class TeeingDemo {

  public static void main(String[] args) {
     List<Integer> listOfNumbers = List.of(10, 25, 9, 87, 56);
     Double average = listOfNumbers.stream()
                        .collect(Collectors.teeing(
                           Collectors.counting(), 
                           Collectors.summingDouble(n -> n), 
                           (count, sum) -> sum/count));
     System.out.println("Average of elements in the list- " + average);
  }
}
Output
Average of elements in the list- 37.4

3. Using Collectors.teeing() to get the employees with the maximum and minimum salaries from the List of Employee objects.

Employee class used is as given below.

public class Employee{
  private String name;
  private String dept;
  private int salary;
  private int age;
  Employee(String name, String dept, int salary, int age){
    this.name = name;
    this.dept = dept;
    this.salary = salary;
    this.age = age;
  }
  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getDept() {
    return dept;
  }
  public void setDept(String dept) {
    this.dept = dept;
  }
  public int getSalary() {
    return salary;
  }
  public void setSalary(int salary) {
    this.salary = salary;
  }
}

To get the maximum and minimum values Collectors.maxBy() and Collectors.minBy() methods are used.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class TeeingDemo {

  public static void main(String[] args) {
    List<Employee> empList =  getEmployeeList();
    List<Optional<Employee>> list = empList.stream()
                       .collect(Collectors.teeing(
                         Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)), 
                         Collectors.minBy(Comparator.comparingInt(Employee::getSalary)), 
                            (emp1, emp2) -> {
                               List<Optional<Employee>> l = new ArrayList<>();
                                 l.add(emp1);
                                 l.add(emp2);
                                 return l;
                            }));
     System.out.println("Employee with max salary- " + (list.get(0).isPresent()? list.get(0).get().getName():null));
     System.out.println("Employee with min salary- " + (list.get(1).isPresent()? list.get(1).get().getName():null));
  }
  
    // Method to create list of employee objects
    private static List<Employee> getEmployeeList(){
        List<Employee> empList = Arrays.asList(new Employee("Ram", "IT", 12000, 34), 
                                       new Employee("Tina", "HR", 15000, 42), 
                                       new Employee("Roger", "IT", 9000, 25), 
                                       new Employee("Troy", "Accounts", 7000, 35));
        
        return empList;
    }
}
Output
Employee with max salary- Tina
Employee with min salary- Troy

That's all for the topic Java Stream Collectors.teeing() Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

September 15, 2021

Java Stream Collectors.partitioningBy() Examples

In this tutorial we’ll see examples of Collectors.partitioningBy() method which is part of the Collectors class in the Java Stream API.

Collectors.partitioningBy() method partitions the input elements according to a passed Predicate (which defines the condition for partitioning), and organizes them into a Map<Boolean, List> with values assigned to two keys "false" and "true" based on whether the input element passes the condition or not.

There are two overloaded Collectors.partitioningBy() methods-

1. Collector<T,?,Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)- Partitions the input elements according to the passed Predicate.

2. Collector<T,?,Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)- In this method along with a Predicate another Collector is also passed as an argument that reduces the values in each partition, and organizes them into a Map<Boolean, D> whose values are the result of the downstream reduction.

Collectors.partitioningBy() Java examples

1. In this simple example we’ll use the partitioningBy() method to partition the list of integers into a map of even and odd numbers.

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

public class PartitioningDemo {

  public static void main(String[] args) {
    List<Integer> listOfNumbers = Arrays.asList(10, 25, 9, 87, 56, 2, 31);
    Map<Boolean, List<Integer>> numbers = listOfNumbers.stream()
                               .collect(Collectors.partitioningBy(n -> n%2 == 0));
    // false key - returns list with odd numbers
    System.out.println("Odd Numbers- " + numbers.get(false));
    // true key - returns list with even numbers
    System.out.println("Even Numbers- " + numbers.get(true));
  }
}
Output
Odd Numbers- [25, 9, 87, 31]
Even Numbers- [10, 56, 2]

In the example n -> n%2 == 0 is an implementation of the Predicate functional interface using lambda expression.

2. Partition a list of Students into those who are studying science and those who are not.

Student class used is as given below
public class Student {
  private int rollNo;
  private String name;
  private String stream;
  private int marks;
  Student(int rollNo, String name, String stream, int marks){
    this.rollNo = rollNo;
    this.name = name;
    this.stream = stream;
    this.marks = marks;
  }
  public int getRollNo() {
    return rollNo;
  }
  public void setRollNo(int rollNo) {
    this.rollNo = rollNo;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getStream() {
    return stream;
  }
  public void setStream(String stream) {
    this.stream = stream;
  }
  public int getMarks() {
    return marks;
  }
  public void setMarks(int marks) {
    this.marks = marks;
  }
  @Override
  public String toString() {
    return "Roll Number: " +  getRollNo() + " Name: " + getName();
  }
}
public class PartitioningDemo {

  public static void main(String[] args) {
      List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
              new Student(2, "Ram", "Science", 99),
              new Student(3, "Priscilla", "Art", 68),
              new Student(4, "Mahesh", "Art", 62),
              new Student(5, "Scott", "Commerce", 72));
    // List with resulting elements
    Map<Boolean, List<Student>> numbers = studentList.stream()
                             .collect(Collectors.partitioningBy(s -> s.getStream().equals("Science")));

    System.out.println("In Science stream- " + numbers.get(true));
    System.out.println("Not in Science stream- " + numbers.get(false));
  }
}
Output
In Science stream- [Roll Number: 1 Name: Peter, Roll Number: 2 Name: Ram]
Not in Science stream- [Roll Number: 3 Name: Priscilla, Roll Number: 4 Name: Mahesh, Roll Number: 5 Name: Scott]

3. Using the partitioningBy() method with two arguments. If you want to get the count of students studying science and those who are not then you can pass Collectors.counting() as the second argument.

public class PartitioningDemo {

  public static void main(String[] args) {
      List<Student> studentList = Arrays.asList(new Student(1, "Peter", "Science", 75),
              new Student(2, "Ram", "Science", 99),
              new Student(3, "Priscilla", "Art", 68),
              new Student(4, "Mahesh", "Art", 62),
              new Student(5, "Scott", "Commerce", 72));

    Map<Boolean, Long> numbers = studentList.stream()
                        .collect(Collectors.partitioningBy(s -> s.getStream().equals("Science"),
                             Collectors.counting()));

    System.out.println("Count of students in Science stream- " + numbers.get(true));
    System.out.println("Count of students not in Science stream- " + numbers.get(false));
  }
}
Output
Count of students in Science stream- 2
Count of students not in Science stream- 3

That's all for the topic Java Stream Collectors.partitioningBy() Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like