Lambda Expressions and Streams
40 minLambda expressions (Java 8+) enable functional programming in Java by providing a concise way to represent anonymous functions. They allow you to pass behavior as method arguments, making code more expressive and flexible. Lambda syntax `(parameters) -> expression` is much more concise than anonymous inner classes. Lambdas work with functional interfaces (interfaces with a single abstract method). Understanding lambdas enables you to write more modern, expressive Java code.
Streams provide a declarative way to process collections of objects. They enable functional-style operations like filtering, mapping, reducing, and collecting without explicit loops. Streams are lazy (operations don't execute until a terminal operation is called) and can be parallelized easily. Stream operations are chainable, creating pipelines that transform data. Understanding streams enables you to write concise, readable data processing code.
Common stream operations include `filter()` (select elements matching a condition), `map()` (transform elements), `reduce()` (aggregate values), `collect()` (gather results into collections), `forEach()` (perform action on each element), `findFirst()`/`findAny()` (find elements), and `sorted()` (sort elements). These operations can be chained to create powerful data processing pipelines. Understanding stream operations enables effective data manipulation.
Method references provide an even more concise syntax than lambdas when you're calling an existing method. They use the `::` operator: `String::toUpperCase`, `System.out::println`, `Integer::parseInt`. Method references can reference static methods, instance methods, and constructors. They're often more readable than equivalent lambdas. Understanding method references helps you write even more concise, readable code.
Functional interfaces are interfaces with a single abstract method. Java provides many built-in functional interfaces: `Function` (transform input to output), `Predicate` (test condition), `Consumer` (consume value), `Supplier` (provide value), and more. Lambdas automatically implement functional interfaces. Understanding functional interfaces helps you use lambdas effectively with existing APIs and create your own functional-style code.
Best practices include using streams for collection processing, using method references when they're more readable than lambdas, understanding that streams are lazy (operations execute only when needed), using parallel streams for large datasets when appropriate, and keeping lambda expressions concise and readable. Lambdas and streams enable functional programming patterns in Java, making code more expressive and maintainable.
Key Concepts
- Lambda expressions provide concise syntax for anonymous functions.
- Streams enable functional-style collection processing.
- Stream operations (filter, map, reduce) can be chained.
- Method references provide concise syntax for existing methods.
- Functional interfaces are interfaces with a single abstract method.
Learning Objectives
Master
- Creating and using lambda expressions
- Working with streams for collection processing
- Using common stream operations (filter, map, reduce, collect)
- Understanding method references and functional interfaces
Develop
- Functional programming thinking
- Understanding declarative vs imperative programming
- Designing expressive, maintainable data processing code
Tips
- Use streams for collection processing—more concise than loops.
- Use method references when they're more readable than lambdas.
- Chain stream operations to create data processing pipelines.
- Use parallel streams for large datasets when appropriate.
Common Pitfalls
- Using streams for simple operations that loops handle better.
- Not understanding stream laziness, causing unexpected behavior.
- Modifying collections during stream processing, causing errors.
- Overusing lambdas, making code harder to read.
Summary
- Lambda expressions enable functional programming in Java.
- Streams provide declarative collection processing.
- Stream operations can be chained into pipelines.
- Method references provide concise syntax for existing methods.
- Understanding lambdas and streams enables modern Java programming.
Exercise
Use lambda expressions and streams to process a list of numbers.
import java.util.*;
import java.util.stream.Collectors;
public class LambdaStreams {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers: " + evenNumbers);
// Map to squares
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("Squares: " + squares);
// Sum of all numbers
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println("Sum: " + sum);
// Find maximum
Optional<Integer> max = numbers.stream()
.max(Integer::compareTo);
System.out.println("Maximum: " + max.orElse(0));
}
}