Examples of lambda expressions used with forEach(), removeIf(), replaceAll() and sort() functions

In this tutorial I will cover the use of lambda expressions with the Collection’s functions forEach(), removeIf(), replaceAll() and sort() . The Lambda Expressions can replace a type of Functional Interface.

There are several Functional interfaces included in Java 8, located in the package java.util.function. The choice of any of these FIs depends on the situation. For example, in our tutorial, each of the mentioned functions takes a specific type type of Functional Interface (BiConsumer, Predicate, etc) .

forEach

Use case with java.util.Map

When used with a Map, the forEach() function takes in parameter an object of type BiConsumer, which is a specialization of the Consumer functional interface.

As a Map element is defined by a key and a value, the BiConsumer FI represents an operation that accepts two input arguments and returns no result.

So, for this example I will create a map that contains  the names of some of Real Madrid soccer team players as values, and their numbers as keys. Then, I will iterate through the players using the forEach function and display each one prefixed by his number.

Map<Integer, String> players = new HashMap<Integer, String>();
players.put(2, "Dani Carvajal");
players.put(19, "Achraf Hakimi");
players.put(4, "Sergio Ramos");
players.put(12, "Marcelo");
players.put(8, "Toni Kroos");
players.put(7, "Christano Ronaldo");

Now I will call the forEach() function from the players Map. I will create a functional interface of type BiConsumer to hold the lambda expression that will print each player.

BiConsumer<Integer, String> consumer = (k,v)-> System.out.println(k + " : "+ v);
players.forEach(consumer);

This can be simplified to only one line like the following :

players.forEach((k,v)-> System.out.println(k + " : "+ v));

The execution will print the following list:

2 : Dani Carvajal
19 : Achraf Hakimi
4 : Sergio Ramos
7 : Christano Ronaldo
8 : Toni Kroos
12 : Marcelo

 

Use case with java.util.List

For this example, I will use the same list of players but without the keys (the numbers) as I will be using a List instead of a Map.

When used with a List, the method forEach() takes an object of type Consumer as parameter.

List<String> players = new ArrayList<String>();
players.add("Dani Carvajal");
players.add("Achraf Hakimi");
players.add("Sergio Ramos");
players.add("Marcelo");
players.add("Toni Kroos");
players.add("Christano Ronaldo");

Then I will create my lambda expression of type Consumer that will take a parameter of type String and print it on the Console. The Consumer object ( or the lambda expression) will be passed as parameter to the forEach() function in order to display the player names.

Consumer<String> consumer = (player)-> System.out.println(player);
players.forEach(consumer);

This will print the following :

Dani Carvajal
Achraf Hakimi
Sergio Ramos
Marcelo
Toni Kroos
Christano Ronaldo

Of course, I could use only one line to simplify the code:

players.forEach((player)-> System.out.println(player));

 

removeIf

The remove if function takes an object of type java.util.function.Predicate as parameter.

A Predicate is a Functional Interface that represents a boolean-valued function of one argument. It means that the Lambda Expression I will create will take a parameter and return a boolean.

To show the use of the removeIf() function, I will create the same list but I will do an intentional mistake : I will add Messi the list of Real Madrid players, so I will use the removeIf function to delete the players with a name equal to Messi, then I will print the List.

// create the list
List<String> players = new ArrayList<String>();
players.add("Dani Carvajal");
players.add("Achraf Hakimi");
players.add("Sergio Ramos");
players.add("Marcelo");
players.add("Toni Kroos");
players.add("Christano Ronaldo");
players.add("Messi");

// create the lambda expression of type Predicate
Predicate<String> predicate = player -> player.equals("Messi");
players.removeIf(predicate);

// display the players
players.forEach((player)-> System.out.println(player));

The result of execution will be a list without Messi !

Dani Carvajal
Achraf Hakimi
Sergio Ramos
Marcelo
Toni Kroos
Christano Ronaldo

replaceAll

The replaceAll() function takes as parameter a Functional Interface of type UnaryOperator when used with a List. A UnaryOperator represents an operation on a single operand that produces a result of the same type as its operand.

In this example, and for a reason I don’t know, I will replace all the name of Christiano Ronaldo by ********* . Practicality, My Lambda Expression will take a parameter of type String  and return a value of the same time but after the replacement by ******** is done.

// create the list
List<String> players = new ArrayList<String>();
players.add("Dani Carvajal");
players.add("Achraf Hakimi");
players.add("Sergio Ramos");
players.add("Marcelo");
players.add("Toni Kroos");
players.add("Christano Ronaldo");

// create the lambda expression
UnaryOperator<String> unaryOperator = 
  s->{
   if(s.equals("Christano Ronaldo")){
    return "******";
   }else{
    return s;
   }
  };

// do the replacement 
players.replaceAll(unaryOperator);

// display the list
players.forEach((player)-> System.out.println(player));

The result will be as follow :

Dani Carvajal
Achraf Hakimi
Sergio Ramos
Marcelo
Toni Kroos
******

sort

The Collection’s sort() method takes two objects as parameter :

  1. The list being sorted;
  2. A Functional Interface of type Comparable (say an Interface of type Comparable for java 7 and bellow).

In previous versions of Java, we had to implement the Interface Comparable and override the compare() method in order to implement the way in which we want the comparison to be done.

In Java 8 however, All we have to do is to pass a Lambda Expression as second parameter, defining how the comparison is done.

in this example I will sort a list of players based on their score.

I will first create a Player class .

class Player {
   
   private String name;
   private Integer score;

   // Constructor with parameters
   public Player(String name, int score){
    this.name = name;
    this.score = score;
   }
   
   public String getName() {
    return name;
   }
   
   public void setName(String name) {
    this.name = name;
   }
   
   public Integer getScore() {
    return score;
   }
   
   public void setScore(Integer score) {
    this.score = score;
   }
   
   @Override
   public String toString() {
    return "[name : "+this.name+", score : "+this.score+"]";
   }
   
}

Then I will create a list of players with their scores, and passing  two parameters to the sort( ) function :

  1. The list to sort;
  2. a Lambda Expression determining how the comparison is done.
// Create a list of players
List<Player> playerList = new ArrayList<Player>();

// add some players, and their scores
playerList.add(new Player("Messi",500));
playerList.add(new Player("Ronaldo",750));
playerList.add(new Player("Roberto",900));
playerList.add(new Player("Albert",450));
playerList.add(new Player("Michel",750));
playerList.add(new Player("Mario",750));

// 1-Define how the sorting will be done in the Lambda Expression (Second parameter)
// 2-sort
Collections.sort(playerList, (p1, p2) -> p1.getScore().compareTo(p2.getScore()) );

// display the sorted list of players (ascendent order)
System.out.println(playerList);

The result of the execution will be as follow :

[[name : Albert, score : 450], [name : Messi, score : 500], [name : Ronaldo, score : 750], [name : Michel, score : 750], [name : Mario, score : 750], [name : Roberto, score : 900]]

 

Streams in java 8

A Stream is a sequence of elements supporting sequential and parallel aggregate operations. Streams have been introduced to Java starting from version 8.

Example of the use of Streams

Consider the following example:

// create a collection of Products
List<Product> products= new ArrayList<Product>();

// calculate the sum of prices of the products whose 
// made locally
// and the price is greater or equal to 10
int sumOfPrices = products.stream()
                      .filter(p -> p.isMadeLocally() == true)
                      .filter(p -> p.getPrice() >=10 )
                      .mapToInt(p -> p.getPrice())
                      .sum();

In the above example we create a stream of products using products.stream() .  Then we filter the products by using the filter() function that takes an object of type Predicate as parameter (in other words, a Functional Interface), meaning that we can use a Lamba expression like in this example.

The mapToInt() function takes a parameter of type ToIntFunction which is also a functional interface, this is why we used the lambda expression p-> p.getPrice() . The mapToInt() function returns an object of type IntStream . And finally the sum() function calculates the sum of all the prices of the products.

Convert a Collection into Stream and vice-versa

filter() functions are sometimes combined with collect() function that converts a stream into a Collection. Example :

// In this example, the products List is converted to a stream, and then to a List
List<Product> listOfProducts = products.stream().filter(p-> p.getPrice()>10).collect(Collectors.toList());

 

Streams Vs Collections

Streams and Collections seem to be similar, but in reality they have different goals: Collections are mainly concerned with the efficient management of, and access to, their elements. However Streams do not give a direct access to their elements or manipulate them, but are instead concerned with declaratively describing their source and the computational operations which will be performed in aggregate on that source.

The BaseStream.iterator() and BaseStream.spliterator() operations can be used to perform a controlled traversal.

Sequential and parallel streams

Stream pipelines can be sequential or parallel. Using the Collection.stream() function will create a sequential stream, and the use of Collection.parallelStream() will create a stream with a parallel mode of execution.

 

Example of replacing anonymous inner class by a Lambda expression in Java 8

One  of the facilities that Java 8 offers is the reduction of the numbers of lines of code in some situations. In this example, we will replace an anonymous inner class by a Lamba Expression. Reducing the code used to compare two players to almost one single line.

First of all, we will need a Player object. A player has a name and a score. Please note that we will use the Integer wrapper type instead of the primitive int type, in order to benefit from the compareTo method offered by the Integer object.

package com.tutoref;

public class Player {
 
 private String name;
 private Integer score;
 
 // Default constructor to allow instanciation without parameters
 public Player(){}
 
 // Constructor with parameters
 public Player(String name, int score){
  this.name = name;
  this.score = score;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public Integer getScore() {
  return score;
 }
 
 public void setScore(Integer score) {
  this.score = score;
 }
 
 
 @Override
 public String toString() {
  // To customise the displaying of a player
  return "[name : "+this.name+", score : "+this.score+"]";
 }
}

 

Comparison using an anonymous inner class

The playerComparator inner class is used to tell the program how to compare two players, based on their score.

In this example we create the playerComparator of type Coparator<Player>, after that create and add some players to the ListArray collection, then we use the Collections.sort method ot sort the players based on their score.

package com.tutoref;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class PlayerSort {

 public static void main(String[] args) {
  
  // Create a comparator object
  Comparator<Player> playerComparator = new Comparator<Player> () {
   @Override
   public int compare(Player player1, Player player2) {
    return player1.getScore().compareTo(player2.getScore());
   }
  };
  
  
  // Create a list of players
  List<Player> playerList = new ArrayList<Player>();
  
  // add some players
  playerList.add(new Player("Messi",500));
  playerList.add(new Player("Ronaldo",750));
  playerList.add(new Player("Roberto",900));
  playerList.add(new Player("Albert",450));
  playerList.add(new Player("Michel",750));
  playerList.add(new Player("Mario",750));
  
  // Sort the list based on the score 
  // using the comparator object
  Collections.sort(playerList,playerComparator);
  
  // display the sorted list of players (in ascendant order)
  System.out.println(playerList);
 }

}

 

COMPARISON Using Lambda Expression

This example will replace the  anonymous inner class, reducing it to one line of code.

package com.tutoref;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PlayerSortUsingLambdaExp {

 public static void main(String[] args) {
  
  // Create a list of players
  List<Player> playerList = new ArrayList<Player>();
  
  // add some players
  playerList.add(new Player("Messi",500));
  playerList.add(new Player("Ronaldo",750));
  playerList.add(new Player("Roberto",900));
  playerList.add(new Player("Albert",450));
  playerList.add(new Player("Michel",750));
  playerList.add(new Player("Mario",750));
  
  Collections.sort(playerList, (p1, p2) -> p1.getScore().compareTo(p2.getScore()) );
  
  // display the sorted list of players (ascendent order)
  System.out.println(playerList);
 }

}

The compactor interface is also a Functional Interface, this is why it can be replaced by a lambda expression. You can imagine that we passed the function making the comparison as parameter.