`

Streams and Lambda Expressions in Java

Streams and Lambda expressions are key features introduced in Java 8 that make Java programming more functional and concise, especially when dealing with collections and bulk data operations.


1. Streams in Java

What is a Stream?

A Stream in Java is a sequence of elements that can be processed in parallel or sequentially. Streams allow operations like filtering, mapping, and reducing on collections of data without altering the original data.

Key Features of Streams:


Types of Stream Operations

1. Intermediate Operations:

These return another Stream and are lazy (executed only when a terminal operation is called).

2. Terminal Operations:

These trigger the execution of the stream pipeline.


Stream Example:

Example 1: Filtering and Mapping

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Tom", "Tim");

        // Filter names starting with 'T' and convert them to uppercase
        List<String> result = names.stream()
                .filter(name -> name.startsWith("T"))
                .map(String::toUpperCase)
                .collect(Collectors.toList());

        System.out.println(result); // Output: [TOM, TIM]
    }
}

Example 2: Reducing a List

import java.util.Arrays;
import java.util.List;

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Sum of all numbers
        int sum = numbers.stream()
                .reduce(0, Integer::sum);

        System.out.println("Sum: " + sum); // Output: Sum: 15
    }
}

2. Lambda Expressions in Java

What is a Lambda Expression?

A Lambda Expression is a concise way to represent an anonymous function. It enables functional programming by treating functions as first-class citizens.

Syntax of Lambda Expression:

(parameters) -> expression

(parameters) -> { 
    // multiple statements
}

Key Features of Lambda Expressions:


Using Lambda Expressions

1. Functional Interfaces

A functional interface is an interface with a single abstract method (SAM). Some examples include:

Example 1: Comparator with Lambda

import java.util.Arrays;
import java.util.List;

public class LambdaComparator {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Tom", "Tim");

        // Sort names in reverse order using Lambda
        names.sort((a, b) -> b.compareTo(a));

        System.out.println(names); // Output: [Tom, Tim, John, Jane]
    }
}

Example 2: Using Predicate

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Predicate to check if a number is even
        Predicate<Integer> isEven = n -> n % 2 == 0;

        numbers.stream()
                .filter(isEven)
                .forEach(System.out::println); // Output: 2 4
    }
}

3. Streams + Lambda Expressions

The real power of streams is unlocked with lambda expressions, allowing concise and functional-style operations.

Example 1: Combine Filter, Map, and Reduce

import java.util.Arrays;
import java.util.List;

public class StreamLambdaExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Find the sum of squares of even numbers
        int sumOfSquares = numbers.stream()
                .filter(n -> n % 2 == 0)      // Filter even numbers
                .map(n -> n * n)             // Square each number
                .reduce(0, Integer::sum);    // Sum them up

        System.out.println("Sum of squares: " + sumOfSquares); // Output: 20
    }
}

Example 2: Grouping with Collectors

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

public class GroupingExample {
    public static void main(String[] args) {
        List<String> items = Arrays.asList("apple", "banana", "cherry", "apple", "banana", "cherry", "cherry");

        // Group items by their frequency
        Map<String, Long> frequency = items.stream()
                .collect(Collectors.groupingBy(item -> item, Collectors.counting()));

        System.out.println(frequency); // Output: {banana=2, cherry=3, apple=2}
    }
}

4. Advantages of Streams and Lambda


5. Parallel Streams

You can process streams in parallel using parallelStream(), which divides tasks into multiple threads.

Example:

import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        numbers.parallelStream()
                .forEach(n -> System.out.println(Thread.currentThread().getName() + ": " + n));
    }
}