# Modern Java Features: Lambdas & Streams

## Part 1: Understanding **Lambdas** for Beginners

### 🤔 What’s a Lambda?

A **lambda expression** is a **short way to write anonymous functions (a function without a name)**:

### 📝 Basic Syntax

```java
(parameters) -> { body }
```

Or shorter if it's one line:

```java
(param) -> doSomething
```

Lambdas are meant to be simple and short, though you can define multi-line lambdas very often they are one-liners. When you use one-liner lambdas code tends to be more readable and cleaner. Check out this example:

### ✅ Example: Sorting with Lambdas

Before Lambdas:

```java
Collections.sort(list, new Comparator<String>() {
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});
```

With Lambdas:

```java
Collections.sort(list, (a, b) -> a.compareTo(b));
```

So much cleaner!

Here’s another example:

```java
List<String> names = List.of("Alice", "Bob", "Charlie");

names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);
```

> `name -> name.startsWith("A")` is a lambda — a function that takes `name` and returns a boolean.

**Lambdas in Java enable functional programming by allowing you to treat behavior as data — passing functions around just like variables or objects.** This leads to more concise, expressive, and modular code, especially when working with streams and callbacks.

## Part 2: Getting to Know the Stream API

### What is the Java Stream API?

The **Java Stream API** is a feature introduced in **Java 8** that allows you to **process collections of data in a functional and declarative style**.

Instead of writing loops to iterate over data, you can use **streams** to **filter**, **transform**, **group**, or **aggregate** data with clean and expressive code.

### ✅ Filter - Keep only the elements that match a condition

**Goal:** Select items that meet a criterion (like an `if` condition in a loop)

```java
List<String> names = List.of("Alice", "Bob", "Charlie");

names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);
```

📤 Output:

```bash
Alice
```

### ✅ Transform (Map) convert elements into something else:

**Goal:** Apply a transformation to each element (like modifying values in a loop)

```java
List<String> words = List.of("java", "stream", "lambda");

words.stream()
     .map(String::toUpperCase)
     .forEach(System.out::println);
```

📤 Output:

```bash
JAVA  
STREAM  
LAMBDA
```

### ✅ 3. **Group** — organize elements by a shared property

```java
 List<String> names = List.of("Maria", "Juan", "Abigail", "Pedro", "Amalia", "Pamela", "Ana");
 Map<Character, List<String>> initialCharacterToNames =
        names.stream().collect(Collectors.groupingBy(name -> name.charAt(0)));
    
 System.out.println(initialCharacterToNames);
```

📤 Output:

```bash
{P=[Pedro, Pamela], A=[Abigail, Amalia, Ana], J=[Juan], M=[Maria]}
```

### ✅ 4. **Aggregate (Reduce)** — combine elements into a single result

**Goal:** Collapse a stream into one value (e.g. sum, min, max, join)

```java
List<Integer> numbers = List.of(1, 2, 3, 4, 5);

int sum = numbers.stream()
                 .reduce(0, Integer::sum);

System.out.println(sum);
```

📤 Output:

```bash
15
```

## 🔧 Part 3: Reviewing the Java Stream Pipeline (Simplified)

Now that we've seen how streams can be used to **filter**, **transform**, **group**, and **aggregate** data, you might be wondering:

> **How do all these operations fit together behind the scenes?**

That's where the **Stream pipeline** comes in. A **stream pipeline** is a sequence of steps through which data flows. Each step performs an operation, like filtering, transforming, or collecting.

Think of it like **an assembly line**: data goes in → gets processed step by step → a result comes out.

This pipeline facilitates performing various steps with the data and finalizing with a result.

### ✅ A Stream Pipeline Has 3 Parts:

| Part | Description | Example |
| --- | --- | --- |
| **Source** | Where the data comes from (e.g. List, Set, Array) | `List.of("a", "b", "c")` |
| **Intermediate Operations** | Steps that transform the data (return a Stream) | `.filter(...)`, `.map(...)` |
| **Terminal Operation** | Ends the pipeline and returns a result | `.collect(...)`, `.forEach(...)` |

### 🎯 Example:

```bash
List<String> names = List.of("Alice", "Bob", "Charlie");

names.stream()                              // ✅ Source
     .filter(name -> name.length() > 3)     // 🔁 Intermediate
     .map(String::toUpperCase)              // 🔁 Intermediate
     .forEach(System.out::println);         // 🏁 Terminal
```

📤 Output:

```bash
ALICE  
CHARLIE
```

### ✅ Conclusion

As you've seen, **lambdas** and the **Stream API** introduce a powerful way to write **cleaner**, **more expressive**, and **more maintainable** Java code. These features are a gateway to functional programming in Java — a style that leads to **fewer bugs**, **more modular logic**, and **better performance** in many cases.

Here are 3 solid reasons to start using them in your everyday coding:

* ✅ Cleaner, more maintainable code than traditional loops
    
* ✅ Encourage immutability — especially helpful in multi-threaded applications.
    
* ✅ Built-in support for parallel processing with `.parallelStream()`: – opening the door to faster execution with minimal effort.
    

Whether you're just beginning with modern Java or looking to sharpen your understanding, mastering lambdas and streams is a must.

📌 Stay tuned for the next part of this series, where we'll continue exploring [**Modern Java Features**](https://code-like-a-woman.hashnode.dev/series/modern-java) — one feature at a time.
