My most used Java 8 Lambda functions
Tags: java, functional, beginners
Published: 2020-06-27 18:11:22

# Introduction

Java 8 launched one of its best features: lambda functions - the first step of Java towards functional programming.  Basically it means that functions can be created without a class, and also can be passed around through methods.

Lambda functions help quite a lot when dealing with collections. Instead of going through loops in order to manipulate their values, lambda functions provide a quick, and a much more readable manner to manipulate them. Going through all the details as how they work, would take much more than one blog post. Therefore here, it will be shown the most common used functions, at least in my humble experience.

# Filtering data

By far, the must useful task one could do with a collection is filter it. And this could be accomplished via the following code:

```java
List<Integer> ints = Arrays.asList(12, 32, 1, 3, 5, 3, 10, 8, 100);

List<Integer> filteredElements = ints.stream().filter(element -> element > 10)
        .collect(Collectors.toList());
```
As one can observe, the code is quite straightforward: the `ints` list of integers is created, then the `stream()` method is called on it. This provides the possibility of using functional programming, already built-in. Here the `filter(...)` method is used, and it does the following:
- for each element in the collection of `ints`, it applies a boolean function. In this case it checks whether each item is higher than 10: `element > 10`. When this is true, the element is passed on to the handled by the next functional method.

Next, the `.collect(Collectors.toList())` function is applied, which transforms the result of the `filter(...)` into a list of Integers, represented by the variable: `filteredElements`. Below is the result of printing the resulting collection (helper methods are used to nicely print the results in the article, but they are not listed, because they are quite simple):

```
Filter Integers higher than 10: [12, 32, 100]
```
Note how easier and explicit is the usage of the `filter(...)` method. Only by a first glance, the developer can understand the purpose of the method. This is much easier to read than the interaction through a collection of integers, and the addition to a new list only the elements which are higher than 10. Also, it is less verbose.

The filter method can also be applied to object parameters, as displayed below:

```java
List<Person> peopleList = Person.createPersonList();

    List<Person>
        filteredPersonList =
        peopleList.stream().filter(person -> person.getAge() > 30).collect(Collectors.toList());
```
Here it is filtered from a list, only elements of the object `Person` which has the `age` parameter higher than 30. The object person is defined below (observe that the function `createPersonList()` is also displayed):

```java
public class Person {
  private int age;
  private String name;

  public Person(int age, String name) {
    this.age = age;
    this.name = name;
  }

  @Override
  public String toString() {
    return "Person{" +
        "age=" + age +
        ", name='" + name + '\'' +
        '}';
  }

  @Override
  public int hashCode() {
    return Objects.hash(age, name);
  }

  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 boolean isPersonCool() {
    return this.age > 40;
  }

  public static List<Person> createPersonList() {
    List<Person> personList = new ArrayList<>();

    personList.add(new Person(18, "Little person"));
    personList.add(new Person(25, "Young adult"));
    personList.add(new Person(33, "Responsible adult"));
    personList.add(new Person(45, "Almost grandfather"));
    personList.add(new Person(60, "Mein Opa"));

    return personList;
  }
}
```

Still on the subject of filtering, one can apply any `boolean` function to apply as a `filter`. Here, the function `isPersonCool` (implemented above) will be applied:

```java
List<Person> peopleList = Person.createPersonList();

    List<Person>
        filteredPersonList =
        peopleList.stream().filter(Person::isPersonCool).collect(Collectors.toList());
```
Note how the notation `Cass::function` can be used to simplify the notation, when the boolean function is applied as `Person::isPersonCool`. This improves readability and leaves the code cleaner. Since someone cool is a person older than 40, the result of the function above is:

```
Filter by cool people: [Person{age=45, name='Almost grandfather'}, Person{age=60, name='Mein Opa'}]
```
# Mapping data

Another useful task for lists, is to map from one object to another. Such activity can also easily be accomplished using lambda functions. In the next example, a list of `Person` objects, will be mapped to `Superhero` objects. Below is the `Superhero` class implementation:

```java
public class Superhero {
  private int age;
  private String name;
  private String superPower;

  public Superhero(int age, String name, String superPower) {
    this.age = age;
    this.name = name;
    this.superPower = superPower;
  }

  @Override
  public String toString() {
    return "Superhero{" +
        "age=" + age +
        ", name='" + name + '\'' +
        ", superPower='" + superPower + '\'' +
        '}';
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getSuperPower() {
    return superPower;
  }

  public void setSuperPower(String superPower) {
    this.superPower = superPower;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
}
```
And here is the `map(...)` usage example:

```java
List<Superhero> superheroes = personList
        .stream()
        .map(person -> new Superhero(person.getAge(), person.getName(), "fly")).collect(
        Collectors.toList());
```
In the code above, here is what happens: 
- for each instance of the `Person` object, a new instance of `Superhero` is created, with the parameters `age` and `name` from the related `Person`, and with the third parameter `fly` as a superpower. 
- Each instance of `Superhero` is then passed to the next lambda function to be worked.
- Then, the function `collect(Collectors.toList())` is applied. It gathers the result of the previous transformation (mapping) and put into a `List` of `Superhero` objects.

The result of the transformation, is depicted below:

```
Map ordinary people to superheroes: [Superhero{age=18, name='Little person', superPower='fly'}, Superhero{age=25, name='Young adult', superPower='fly'}, Superhero{age=33, name='Responsible adult', superPower='fly'}, Superhero{age=45, name='Almost grandfather', superPower='fly'}, Superhero{age=60, name='Mein Opa', superPower='fly'}]
```
# Filtering and mapping data

So far filtering and mapping of data have been applied separately. Nothing forbids, however, for them to be used together, as the example below shows:

```java
List<Superhero> coolSuperheroes =
        Person.createPersonList().stream()
            .filter(Person::isPersonCool)
            .map(coolPeople -> new Superhero(coolPeople.getAge(), coolPeople.getName(), "be invisible"))
            .collect(Collectors.toList());
```
In the example above, a list of `People` are filtered by the criteria, of who is cool, by the usage of the function `Person::isPersonCool`. Later, all *cool people* are transformed into *superheroes*, by the usage of mapping: `.map(coolPeople -> new Superhero(coolPeople.getAge(), coolPeople.getName(), "be invisible"))`. The final step is to collect the list of `Superhero` objects via `.collect(Collectors.toList())`. The result of the discussed code can be seen below:

```
Filter by coolness and map to superheroes: [Superhero{age=45, name='Almost grandfather', superPower='be invisible'}, Superhero{age=60, name='Mein Opa', superPower='be invisible'}]
```

# Gathering data

Making arithmetic with collections data is another common activity in a collection. Of course, this is also supported by lambda functions.

Below is an example where all elements of an integer list are summed:

```java
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

    int sumResult = integers.stream().mapToInt(Integer::intValue).sum();
```
In the above example, firstly the list of `integers` calls the `stream()` method, in order to have access to the lambda functions. Then, each element is mapped to an integer. By doing so, one has access to the `sum()` function, which, as its name suggests, sum all elements in the mapped function. The result is shown below:

```
Sum ints using IntStream.sum(): 45
```
One interesting function which one can use to sum all elements in a list, is the `reduce`. It does the following:
- for a list `l`, there is an accumulator value `a`. Each element of the list is called `e`.
 - a reduction function `r` is applied to each element `e`.  The function `r` has as its first parameter the accumulator`a` and the current value of the list `e`. The reduction function has as a returning value, the new accumulator `a` which is the result of whichever algorithm was used on the reduction function `r`.

Indeed the definition above is quite generic, and hard to grasp. Therefore, see the example below, which will make things much more clearer:

```java
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

    int sumResult = integers.stream().reduce(0, (a, e) -> a + e);```

Note the reduce function `r` sums the previous accumulator value `a` with the current element `e`. So, for each element of the list, its value is summed to the total already calculated before. The initial value for the accumulated value `a` is zero, as defined here: `reduce(0, (a, e) -> a + e)`.

Other interesting functions are provided, such as calculating the average of a list of numbers:

```java
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

    OptionalDouble meanResult = integers.stream().mapToInt(Integer::intValue).average();
```
Calculating the maximum value of a list:

```java
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

    OptionalInt maxResult = integers.stream().mapToInt(Integer::valueOf).max();
```
And of course, calculating the minimum:

```java
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

    OptionalInt minResult = integers.stream().mapToInt(Integer::valueOf).min();
```
# Filtering, mapping and gathering data altogether

As expected, all the three types of lambda functions discussed can be put together, as in the example below:

```java
OptionalDouble averageAge = Person.createPersonList().stream()
        .filter(Person::isPersonCool)
        .map(coolPeople -> new Superhero(coolPeople.getAge(), coolPeople.getName(), "be invisible"))
        .mapToInt(Superhero::getAge)
        .average();
```
Here, a collection of `Person` objects, is filtered by coolness. Next, they are mapped to a list of `Superhero` objects, for then having the average of their age calculated.

# Discussions

Please note that here, not all lambda functions were discussed, only the most common ones. Of course, readers may disagree of what is more common, or useful. Regardless, it will be hard to find a developer who does not find those quite useful. Therefore, if you are new to this topic, this would serve as quick and nice starting point.

Moreover, observe how easy it is to understand the meaning of each one of them. Having names describing what exactly is happening, is much readable than to have loops and `if`clauses within them to accomplish the same goal.

You can find the entire source code used in this article [here](https://github.com/dufernandes/blogs-rouce-code/tree/master/devto).