---
# System prepended metadata

title: Java Advanced Concepts - Lambda & Streams

---

---
title: Agenda
description: 
duration: 180
card_type: cue_card
---

## Agenda
* Functional Interfaces and Lambdas
    * Motivation
    * Implementation
* Streams 

**[Message for Instructor]**
* Please give a brief about the agenda

---
title: Functional Interfaces
description: What are functional Interfaces, where have we used it.
duration: 5040
card_type: cue_card
---

* Let's say we want to create a thread. What is the most common way of creating a thread?
    * We have seen using **runnable** interface
    * If you want your thread to return something, then you use **callable**

* Suppose you want to create a class without a name. What is it called?
    * It's called an anonymous function, its a class without a name, and it's an inline class.

Let's implement this in the code.


We are creating a class called SomethingDoer which does nothing but just prints the "Do something."

```java
package com.scaler.lambdasandstreams;

public class SomethingDoer implements Runnable {

    @Override
    public void run() {
        System.out.println("Do something v1");
    }
}
```

We will be using it in our main class as follows: 

```java
SomethingDoer somethingDoer = new SomethingDoer();
        Thread t = new Thread(somethingDoer);
        t.start();
```

**[Message for Instructor:]** Clear all of the doubts if the learners have for this.

**[Ask from learners: ]**
Is it a good way to implement this?? I mean that we just had to implement a method and for that we created a whole class. 

**Till Java-8 we had to do it like this. These are what we called functional interfaces.**

What if we can implement it in the main class. We can now in somewhat this manner: 

```java
 Runnable r = () -> {
            System.out.println("Do something v2");
        };

        Thread t2 = new Thread(r);
        t2.start();
```

This will be running perfectly fine. But can we optimize it further?? 

**Yes,something like this :**

```java
Thread t3 = new Thread(
                () -> {
                    System.out.println("Do something v3");
                }
        );

        t3.start();
```
Now if I even call my Main function all the three thread will be running perfectly fine.

So this is what **lamdas** actually are. Lets understand it in better manner.

---
title: Lambdas
description: What are lambdas and thier advantages.
duration: 5040
card_type: cue_card
---

It is basically a way to create an object of a class that implements an interface with only one method. That is what we are doing here. 

Note: **For 99% of usecases work can be done with or without using the lamdas it is just a way to implement nothing else.**

Lamda function looks something like this: 

```java
() -> {

}
```

We can even pass the parameters also in the lamda function that we will be seeing also.

---
title: Lambdas Implementation
description: Two examples of using Lambda and discussing them in detail.
duration: 5040
card_type: cue_card
---


**[Ask the Learners :]**

Do you guys remember the example of the list of students we were sorting on the previous class. Can we implement lamdas there??

Ans: Yes because were basically creating a comparator class there **StudentComparatorByPsp** and using it in our main class something like this: 

```
 List<Student> students = List.of(
                 new Student(1,20,"Gautam", 30.0),
                 new Student(2,24,"Gautam2", 60.0),
                 new Student(3,28,"Gautam3", 90.0)
         );

         Collections.sort(
                 students, new StudentComparatorByPsp()
         );
```

We can implement it using the lamdas also by something like this: 

```
Collections.sort(
                students, (o1, o2) -> {
                    if (o1.getPsp() < o2.getPsp()) {
                        return -1;
                    }

                    if (o1.getPsp() > o2.getPsp()) {
                        return 1;
                    }

                    return 0;
                }
        );
```

**[For the instructors:]** Explain how we didn't even passed any information of psp even the lamdas was able to get it.

**In Java, when using lambda expressions, the variables from the enclosing scope (including local variables and parameters of the enclosing method or class) are captured and can be used within the lambda expression.**

Lets also undestand it with an example of Calculator: 

**[Ask from learners :]**

How many parameters does the Calcualtor class takes??

Ans-> 2

Lets create a MathematicalOperation Class: 

```java
package com.scaler.lambdasandstreams;

public interface MathematicalOperation {

    Integer operate(Integer a, Integer b);
}
```

Now for the Addition, Subtraction,Mutiplication we can use the different Classes something like this:

```java
package com.scaler.lambdasandstreams;

public class AdditionOperation implements MathematicalOperation {

    @Override
    public Integer operate(Integer a, Integer b) {
        return a + b;
    }
}
```

but its not ideal if we need it only in our main class. We can do it something like this: 

```java

        MathematicalOperation addition = (a, b) -> {
            return a + b;
        };

        MathematicalOperation subtraction = (a, b) -> {
            return a - b;
        };

        MathematicalOperation multiple = (a, b) -> {
            return a * b;
        };

        multiple.operate(2, 3);
```

**[Message for Instrutor :]** Explain this one in great detail.

So we have understood Lamdas and it is just a way **for expressing instances of functional interfaces.**

We will now be moving forward to the Streams. 

---
title: Streams
description: What are streams in real life example.
duration: 5040
card_type: cue_card
---

For understanding the Streams: 

Lets take an example of application that needs to fetch the data from database.

**[Ask the Learners]**
So how can I print the name of all the products from the database??

Ans-> 

What we can do typically is that: 
1. Fetch all of the products from the database.
2. Print the name of those products.

The qseudo will be something like this: 

```java
List Products products = {}; 

for(Product p:products){
    p.getName();
}
```

Now what is the problem with this??

**Think it in this way that I will be printing one name at one time, right??**

and, say that the complete list of product was of 10MB so the entire RAM of 10MB while the entire loop of `getName()` was running.

So what is the solution for this?

The **Streams** provide a solution to this. Think of stream as a **data flow** like a river like you accessed a particular element and you cannot access the past elements and can only access the upcoming element. 

It works something in this way: 

1. Will Start by getting the first object in the list
2. And will keep getting till you ask.

![image](https://hackmd.io/_uploads/r17e1vIq6.png)

That great so basically: 

* Stream closes aftr you have accessed all the elements in it.
* Can't go back in a stream(think of it like a river).

---
title: Advantages of Using Streams
description: Why Streams(What are some of the advantaged of using it)??
duration: 5040
card_type: cue_card
---

1. **Readability and Conciseness :** Java introduced various methods also with the streams that we will be seeing after some time which makes the code much readable and concise too.

2. **Parallel Execution :** Streams make it very easy to implement parallelism we can have parallel lines to managae the data fastly and efficiently.

3. **Lazy Evaluvation :** Stream will not evaluvate an operation unless needed. For example: I can have a list and using the streams I can multiply all the elements by 2 without evaluvation then remove some element with some condition and Streams will do the evaluvation only once we call it. This reuslts in **huge performace gains.**

Lets now see **Streams** in action.

---
title: Implementation of Streams
description: Implementing the Streams with the List and seeing different scenarios
duration: 5040
card_type: cue_card
---

1. Lets understand via the list can be printed with and without stream.

```
package com.example.practice;

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

public class StreamExample {

    public static void main(String[] args) {
        List<Integer> l1 = List.of(
                1, 2, 3, 4
        );

        for (Integer i: l1) {
            System.out.println(i);
        }

        Stream<Integer> s1 = l1.stream();

        s1.forEach(System.out::println);


    }
}
```

**[Note for instructor:]** Spend some time to the code and explain how it improves the readablity and all.

**This will result in printing the elements in the list.**

2. Lets now try to print the elements of the list by multiplying it by 2.

```
s1.forEach((element) -> System.out.println(element*2));
```
We can achieve it with this code which will result: 

![image](https://hackmd.io/_uploads/BkV8fwUqa.png)


Before moving further lets understand that there are two methods in the Stream: 

1. Terminal Method: They will do the work and the stream will not be available I mean I cannot access the elements further.For exmaple **forEach is a terminal method.**

![image](https://hackmd.io/_uploads/rJvJ7DIqT.png)
We can see above **forEach** returning void.

2. Intermediate method: This will return the elements from the stream. For Example: **filter** is a intermediate method

![image](https://hackmd.io/_uploads/HkwXXP896.png)

We defined s1 as stream of integers and it is returning the stream of integers as we can see above.

Let's now see one exmaple and all of the things will be cleared: 

```
package com.example.practice;

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

public class StreamExample {

    public static void main(String[] args) {
        List<Integer> l1 = List.of(
                1, 2, 3, 4,5,6
        );

//        Stream<Integer> s1 = l1.stream();

        l1.stream() // {1, 2, 3, 4, 5, 6}
                .map((elem) -> elem * 3) // {3, 6, 9, 12, 15, 18}
                .filter((elem) -> elem < 10) // {3, 6, 9}
                .forEach(System.out::println);
    }
}
```

We have used: 
1. **map** method that will go to all the elements and will return by making them 3 times.

**[Ask from learners:]**
Why we didn't use **forEach** here?

Ans-> forEach will not return the elements but the map does we need the elements to perform operations.

2. Then use used **filer** to filter the elements that are less that 10.
3. Laslty we are printing those elements.

**[Note for instructor:]** Highlight the **lazy evaluvation** being present in the above code.

So that's all about the streams.