Java Stream API: Lambda, Functional Interfaces, Map, Filter, Reduce & Collect
This guide covers the Java Stream API comprehensively—including lambda expressions, functional interfaces, map, filter, reduce, and collect operations with practical examples.
In a Nutshell
Java Stream API enables functional data processing using lambda expressions. map transforms elements, filter selects them based on conditions, reduce aggregates values, and collect gathers results into containers.
Quick Technical Summary
Java Stream API is a functional API for processing data collections. It supports declarative programming with lambda expressions and functional interfaces.
Key concepts:
Lambda Expressions
- Syntax:
(parameter) -> expressionor(parameter) -> { statements } - Type inference: The type is inferred from context
- Effectively final: Variables must be final or effectively final
- Method references: Shorter notation for lambda expressions
Functional Interfaces
- **Predicate<T>**: boolean test(T t) - Test a condition
- **Function<T,R>**: R apply(T t) - Transform a value
- **Consumer<T>**: void accept(T t) - Consume a value
- **Supplier<T>**: T get() - Supply a value
- **UnaryOperator<T>**: T apply(T t) - Unary operation
- **BinaryOperator<T>**: T apply(T t1, T t2) - Binary operation
Stream Operations
- Intermediate: map, filter, sorted, distinct, limit, skip
- Terminal: forEach, collect, reduce, count, anyMatch, allMatch
- Short-circuiting: findFirst, findAny, anyMatch, allMatch, noneMatch
Exam Focus Points
- Stream API: Functional data processing in Java 8+
- Lambda expressions: Anonymous functions with compact syntax
- Functional interfaces: Interfaces with a single abstract method
- Map: Transforms elements in a stream
- Filter: Selects elements based on predicates
- Reduce: Aggregates stream elements into a single value
- Collect: Gathers results into containers
- Professional certifications: Modern Java, functional programming
Core Components
- Lambda expressions: Compact function literals
- Functional interfaces: Typed function definitions
- Stream creation: From collections, arrays, I/O, generators
- Intermediate operations: Lazy, chainable transformations
- Terminal operations: Eager, finalize stream processing
- Collectors: Specialized collection operations
- Parallel streams: Parallel data processing
- Optional: Null-safe containers for values
Practical Examples
1. Basic Stream Operations
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class StreamGrundlagen {
public static void main(String[] args) {
System.out.println("=== Stream API Grundlagen ===");
// Data source
List<String> namen = Arrays.asList("Alice", "Bob", "Charlie", "Diana", "Eve");
List<Integer> zahlen = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter demo
filterDemo(namen, zahlen);
// Map demo
mapDemo(namen, zahlen);
// Reduce demo
reduceDemo(zahlen);
// Collect demo
collectDemo(namen, zahlen);
// Method references
methodenreferenzenDemo();
}
private static void filterDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Filter Demo ---");
// Lambda expression for filtering
List<String> langeNamen = namen.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
System.out.println("Namen mit > 4 Buchstaben: " + langeNamen);
// Multiple filters
List<Integer> gefilterteZahlen = zahlen.stream()
.filter(zahl -> zahl % 2 == 0) // Even numbers
.filter(zahl -> zahl > 3) // Greater than 3
.collect(Collectors.toList());
System.out.println("Gerade Zahlen > 3: " + gefilterteZahlen);
// Complex predicate
Predicate<String> komplexesPraedikat = name ->
name.startsWith("A") && name.length() <= 5;
List<String> gefilterteNamen = namen.stream()
.filter(komplexesPraedikat)
.collect(Collectors.toList());
System.out.println("Namen mit 'A' und ≤5 Buchstaben: " + gefilterteNamen);
}
private static void mapDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Map Demo ---");
// String to Integer (length)
List<Integer> namenslaengen = namen.stream()
.map(name -> name.length())
.collect(Collectors.toList());
System.out.println("Namenslängen: " + namenslaengen);
// Integer to String (squares)
List<String> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.map(quad -> "Quadrat: " + quad)
.collect(Collectors.toList());
System.out.println("Quadrate: " + quadrate);
// FlatMap for nested structures
List<List<Integer>> verschachtelt = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8, 9)
);
List<Integer> flach = verschachtelt.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
System.out.println("Flachgemacht: " + flach);
// Map with objects
List<Person> personen = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
List<String> personenInfo = personen.stream()
.map(person -> person.getName() + " (" + person.getAlter() + ")")
.collect(Collectors.toList());
System.out.println("Personen-Info: " + personenInfo);
}
private static void reduceDemo(List<Integer> zahlen) {
System.out.println("\n--- Reduce Demo ---");
// Sum with reduce
Optional<Integer> summe = zahlen.stream()
.reduce((a, b) -> a + b);
System.out.println("Summe: " + summe.orElse(0));
// Product with reduce
Optional<Integer> produkt = zahlen.stream()
.reduce((a, b) -> a * b);
System.out.println("Produkt: " + produkt.orElse(1));
// Maximum with reduce
Optional<Integer> maximum = zahlen.stream()
.reduce(Integer::max);
System.out.println("Maximum: " + maximum.orElse(0));
// Reduce with identity value
int summeMitIdentitaet = zahlen.stream()
.reduce(0, Integer::sum);
System.out.println("Summe mit Identität: " + summeMitIdentitaet);
// String concatenation
List<String> woerter = Arrays.asList("Java", "Stream", "API");
Optional<String> verkettet = woerter.stream()
.reduce((a, b) -> a + " " + b);
System.out.println("Verkettet: " + verkettet.orElse(""));
}
private static void collectDemo(List<String> namen, List<Integer> zahlen) {
System.out.println("\n--- Collect Demo ---");
// To list
List<String> grossgeschrieben = namen.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Großgeschrieben: " + grossgeschrieben);
// To set
Set<Integer> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.collect(Collectors.toSet());
System.out.println("Quadrate als Set: " + quadrate);
// To map
Map<String, Integer> namenMap = namen.stream()
.collect(Collectors.toMap(
name -> name, // Key mapper
name -> name.length() // Value mapper
));
System.out.println("Namen-Map: " + namenMap);
// Grouping by
Map<Integer, List<String>> nachLaengeGruppiert = namen.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("Nach Länge gruppiert: " + nachLaengeGruppiert);
// Partitioning by
Map<Boolean, List<Integer>> geradeUngerade = zahlen.stream()
.collect(Collectors.partitioningBy(zahl -> zahl % 2 == 0));
System.out.println("Partitioniert: " + geradeUngerade);
// Joining
String namensliste = namen.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println("Namensliste: " + namensliste);
// Summarizing
IntSummaryStatistics statistik = zahlen.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("Statistik: " + statistik);
}
private static void methodenreferenzenDemo() {
System.out.println("\n--- Methodenreferenzen Demo ---");
List<String> namen = Arrays.asList("alice", "bob", "charlie");
// Static method reference
List<String> grossgeschrieben = namen.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("Statische Referenz: " + grossgeschrieben);
// Instance method reference
List<Integer> laengen = namen.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("Instanz-Referenz: " + laengen);
// Constructor reference
List<Person> personen = namen.stream()
.map(name -> new Person(name, 20 + name.length()))
.collect(Collectors.toList());
System.out.println("Konstruktor-Referenz: " +
personen.stream()
.map(Person::getName)
.collect(Collectors.toList()));
}
// Helper class
static class Person {
private String name;
private int alter;
public Person(String name, int alter) {
this.name = name;
this.alter = alter;
}
public String getName() { return name; }
public int getAlter() { return alter; }
}
}
2. Advanced Stream Operations
import java.util.*;
import java.util.stream.*;
import java.util.function.*;
public class AdvancedStreams {
public static void main(String[] args) {
System.out.println("=== Advanced Stream Operations ===");
// Sample data for demonstrations
List<Student> students = Arrays.asList(
new Student("Alice", "Computer Science", 85, 3),
new Student("Bob", "Mathematics", 92, 2),
new Student("Charlie", "Computer Science", 78, 4),
new Student("Diana", "Physics", 88, 1),
new Student("Eve", "Computer Science", 95, 2),
new Student("Frank", "Mathematics", 73, 3)
);
// Sorting
sortingDemo(students);
// Limit and Skip
limitSkipDemo(students);
// Distinct
distinctDemo();
// Match operations
matchDemo(students);
// Find operations
findDemo(students);
// Optional handling
optionalDemo(students);
// Parallel Streams
parallelStreamDemo(students);
}
private static void sortingDemo(List<Student> students) {
System.out.println("\n--- Sorting Demo ---");
// Sort by grade
List<Student> byGrade = students.stream()
.sorted(Comparator.comparing(Student::getGrade))
.collect(Collectors.toList());
System.out.println("By grade ascending:");
byGrade.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getGrade()));
// Sort by grade descending
List<Student> byGradeDescending = students.stream()
.sorted(Comparator.comparing(Student::getGrade).reversed())
.collect(Collectors.toList());
System.out.println("\nBy grade descending:");
byGradeDescending.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getGrade()));
// Multi-criteria sorting
List<Student> multiCriteria = students.stream()
.sorted(Comparator
.comparing(Student::getSubject)
.thenComparing(Student::getGrade)
.thenComparing(Student::getName))
.collect(Collectors.toList());
System.out.println("\nBy subject, grade, name:");
multiCriteria.forEach(s -> System.out.println(" " + s.getSubject() + " - " +
s.getName() + ": " + s.getGrade()));
}
private static void limitSkipDemo(List<Student> students) {
System.out.println("\n--- Limit and Skip Demo ---");
// First 3 students
List<Student> firstThree = students.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println("First 3 students:");
firstThree.forEach(s -> System.out.println(" " + s.getName()));
// Skip the first 2
List<Student> afterSkip = students.stream()
.skip(2)
.collect(Collectors.toList());
System.out.println("\nAfter skipping first 2:");
afterSkip.forEach(s -> System.out.println(" " + s.getName()));
// Pagination (page 2, 2 elements per page)
int page = 2;
int size = 2;
List<Student> paginated = students.stream()
.skip((page - 1) * size)
.limit(size)
.collect(Collectors.toList());
System.out.println("\nPage " + page + " (size " + size + "):");
paginated.forEach(s -> System.out.println(" " + s.getName()));
}
private static void distinctDemo() {
System.out.println("\n--- Distinct Demo ---");
List<Integer> numbersWithDuplicates = Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5, 1);
List<Integer> uniqueNumbers = numbersWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("With duplicates: " + numbersWithDuplicates);
System.out.println("Unique: " + uniqueNumbers);
// Distinct with objects
List<String> subjects = Arrays.asList("Computer Science", "Mathematics", "Computer Science",
"Physics", "Mathematics", "Computer Science");
List<String> uniqueSubjects = subjects.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("\nSubjects with duplicates: " + subjects);
System.out.println("Unique subjects: " + uniqueSubjects);
}
private static void matchDemo(List<Student> students) {
System.out.println("\n--- Match Demo ---");
// All Match - all satisfy condition
boolean allPassed = students.stream()
.allMatch(s -> s.getGrade() >= 50);
System.out.println("All passed: " + allPassed);
boolean allComputerScience = students.stream()
.allMatch(s -> s.getSubject().equals("Computer Science"));
System.out.println("All Computer Science: " + allComputerScience);
// Any Match - at least one satisfies condition
boolean someComputerScience = students.stream()
.anyMatch(s -> s.getSubject().equals("Computer Science"));
System.out.println("Some Computer Science: " + someComputerScience);
boolean someExcellent = students.stream()
.anyMatch(s -> s.getGrade() >= 90);
System.out.println("Some excellent: " + someExcellent);
// None Match - none satisfy condition
boolean noneFlunked = students.stream()
.noneMatch(s -> s.getGrade() < 50);
System.out.println("None flunked: " + noneFlunked);
}
private static void findDemo(List<Student> students) {
System.out.println("\n--- Find Demo ---");
// Find First - first element
Optional<Student> first = students.stream()
.findFirst();
first.ifPresent(s -> System.out.println("First student: " + s.getName()));
// Find Any - any element (especially useful with parallel streams)
Optional<Student> anyComputerScientist = students.stream()
.filter(s -> s.getSubject().equals("Computer Science"))
.findAny();
anyComputerScientist.ifPresent(s ->
System.out.println("Any Computer Science student: " + s.getName()));
// Find with complex predicate
Optional<Student> bestMathematician = students.stream()
.filter(s -> s.getSubject().equals("Mathematics"))
.max(Comparator.comparing(Student::getGrade));
bestMathematician.ifPresent(s ->
System.out.println("Best mathematician: " + s.getName() + " (" + s.getGrade() + ")"));
}
private static void optionalDemo(List<Student> students) {
System.out.println("\n--- Optional Handling Demo ---");
// Optional with map
Optional<String> firstName = students.stream()
.findFirst()
.map(Student::getName);
firstName.ifPresent(name -> System.out.println("First name: " + name));
// Optional with filter
Optional<Student> topStudent = students.stream()
.max(Comparator.comparing(Student::getGrade));
String topName = topStudent
.filter(s -> s.getGrade() >= 90)
.map(Student::getName)
.orElse("None with 90+ points");
System.out.println("Top student (90+): " + topName);
// Optional chaining
Optional<String> topSubject = students.stream()
.max(Comparator.comparing(Student::getGrade))
.flatMap(s -> Optional.ofNullable(s.getSubject()))
.map(String::toUpperCase);
topSubject.ifPresent(subject ->
System.out.println("Top student's subject: " + subject));
// Optional with supplier
String defaultValue = students.stream()
.filter(s -> s.getName().equals("NonExistent"))
.findFirst()
.map(Student::getName)
.orElseGet(() -> "Default Student");
System.out.println("Default value: " + defaultValue);
}
private static void parallelStreamDemo(List<Student> students) {
System.out.println("\n--- Parallel Stream Demo ---");
// Parallel processing
long startTime = System.currentTimeMillis();
List<String> namesParallel = students.parallelStream()
.filter(s -> s.getGrade() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
long endTime = System.currentTimeMillis();
System.out.println("Parallel result: " + namesParallel);
System.out.println("Parallel time: " + (endTime - startTime) + "ms");
// Comparison with sequential processing
startTime = System.currentTimeMillis();
List<String> namesSequential = students.stream()
.filter(s -> s.getGrade() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
endTime = System.currentTimeMillis();
System.out.println("\nSequential result: " + namesSequential);
System.out.println("Sequential time: " + (endTime - startTime) + "ms");
// Thread info with parallel stream
System.out.println("\nThread info with parallel stream:");
students.parallelStream()
.forEach(s -> System.out.println(s.getName() + " on " +
Thread.currentThread().getName()));
}
// Student class
static class Student {
private String name;
private String subject;
private int grade;
private int semester;
public Student(String name, String subject, int grade, int semester) {
this.name = name;
this.subject = subject;
this.grade = grade;
this.semester = semester;
}
public String getName() { return name; }
public String getSubject() { return subject; }
public int getGrade() { return grade; }
public int getSemester() { return semester; }
}
}
3. Specialized Collectors and Custom Operations
import java.util.*;
import java.util.stream.*;
import java.util.function.*;
public class SpecializedCollectors {
public static void main(String[] args) {
System.out.println("=== Specialized Collectors Demo ===");
// Test data
List<Product> products = Arrays.asList(
new Product("Laptop", "Electronics", 999.99, 5),
new Product("Mouse", "Electronics", 29.99, 15),
new Product("Keyboard", "Electronics", 79.99, 8),
new Product("Book", "Books", 19.99, 20),
new Product("Pen", "Office", 2.99, 50),
new Product("Paper", "Office", 9.99, 30)
);
// Grouping with aggregation
groupingWithAggregation(products);
// Multi-level grouping
multiLevelGrouping(products);
// Custom collector
customCollectorDemo();
// Downstream collectors
downstreamCollectorsDemo(products);
// Primitive streams
primitiveStreamsDemo();
}
private static void groupingWithAggregation(List<Product> products) {
System.out.println("\n--- Grouping with Aggregation ---");
// Group by category with statistics
Map<String, DoubleSummaryStatistics> priceStatistics = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.summarizingDouble(Product::getPrice)
));
priceStatistics.forEach((category, statistics) -> {
System.out.println(category + ":");
System.out.println(" Average: " + statistics.getAverage());
System.out.println(" Minimum: " + statistics.getMin());
System.out.println(" Maximum: " + statistics.getMax());
System.out.println(" Sum: " + statistics.getSum());
});
// Grouping with mapping
Map<String, Set<String>> categoryNames = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.mapping(Product::getName, Collectors.toSet())
));
System.out.println("\nCategories with product names:");
categoryNames.forEach((category, names) ->
System.out.println(category + ": " + names));
// Grouping with filtering
Map<String, List<Product>> expensiveProducts = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.filtering(p -> p.getPrice() > 50, Collectors.toList())
));
System.out.println("\nExpensive products (>50€):");
expensiveProducts.forEach((category, productList) -> {
if (!productList.isEmpty()) {
System.out.println(category + ": " +
productList.stream().map(Product::getName).collect(Collectors.toList()));
}
});
}
private static void multiLevelGrouping(List<Product> products) {
System.out.println("\n--- Multi-Level Grouping ---");
// Group products by price categories
Map<String, Map<String, List<Product>>> multiLevel = products.stream()
.collect(Collectors.groupingBy(
p -> p.getPrice() < 50 ? "Affordable" : "Expensive",
Collectors.groupingBy(Product::getCategory)
));
System.out.println("Multi-level grouping:");
multiLevel.forEach((priceCategory, categoryMap) -> {
System.out.println(priceCategory + ":");
categoryMap.forEach((category, productList) -> {
System.out.println(" " + category + ": " +
productList.stream().map(Product::getName).collect(Collectors.toList()));
});
});
}
private static void customCollectorDemo() {
System.out.println("\n--- Custom Collector Demo ---");
List<String> words = Arrays.asList("Java", "Stream", "API", "Functional", "Programming");
// Custom collector for string concatenation with delimiter and prefix/suffix
Collector<String, StringBuilder, String> customStringCollector = Collector.of(
StringBuilder::new, // Supplier
(builder, str) -> { // Accumulator
if (builder.length() > 0) {
builder.append(" | ");
}
builder.append(str.toUpperCase());
},
StringBuilder::append, // Combiner
StringBuilder::toString, // Finisher
Characteristics.IDENTITY_FINISH
);
String result = words.stream().collect(customStringCollector);
System.out.println("Custom collector result: " + result);
// Custom collector for statistics
Collector<Integer, int[], Double> averageCollector = Collector.of(
() -> new int[2], // [sum, count]
(acc, num) -> {
acc[0] += num; // sum
acc[1]++; // count
},
(acc1, acc2) -> {
acc1[0] += acc2[0];
acc1[1] += acc2[1];
return acc1;
},
acc -> acc[1] == 0 ? 0 : (double) acc[0] / acc[1] // average
);
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
double average = numbers.stream().collect(averageCollector);
System.out.println("Custom average: " + average);
}
private static void downstreamCollectorsDemo(List<Product> products) {
System.out.println("\n--- Downstream Collectors Demo ---");
// GroupingBy with counting
Map<String, Long> countPerCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting()
));
System.out.println("Count per category:");
countPerCategory.forEach((category, count) ->
System.out.println(category + ": " + count));
// GroupingBy with summing
Map<String, Integer> stockPerCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.summingInt(Product::getStock)
));
System.out.println("\nStock per category:");
stockPerCategory.forEach((category, stock) ->
System.out.println(category + ": " + stock));
// GroupingBy with maxBy
Map<String, Optional<Product>> mostExpensivePerCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.maxBy(Comparator.comparing(Product::getPrice))
));
System.out.println("\nMost expensive product per category:");
mostExpensivePerCategory.forEach((category, optional) ->
optional.ifPresent(p -> System.out.println(category + ": " + p.getName())));
// CollectingAndThen for post-processing results
Map<String, String> categoryInfo = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.size() + " products, " +
String.format("%.2f€",
list.stream().mapToDouble(Product::getPrice).average().orElse(0))
)
));
System.out.println("\nCategory info:");
categoryInfo.forEach((category, info) -> System.out.println(category + ": " + info));
}
private static void primitiveStreamsDemo() {
System.out.println("\n--- Primitive Streams Demo ---");
// IntStream
IntStream numbers = IntStream.range(1, 10);
int sum = numbers.sum();
System.out.println("Sum 1-9: " + sum);
// Boxed to object stream
List<Integer> numbersList = IntStream.rangeClosed(1, 5)
.boxed()
.collect(Collectors.toList());
System.out.println("Numbers as list: " + numbersList);
// DoubleStream with calculations
double[] prices = {19.99, 29.99, 99.99, 149.99};
DoubleSummaryStatistics priceStatistics = Arrays.stream(prices)
.summaryStatistics();
System.out.println("\nPrice statistics:");
System.out.println(" Count: " + priceStatistics.getCount());
System.out.println(" Sum: " + priceStatistics.getSum());
System.out.println(" Average: " + priceStatistics.getAverage());
System.out.println(" Min: " + priceStatistics.getMin());
System.out.println(" Max: " + priceStatistics.getMax());
// LongStream for large numbers
long factorial = LongStream.rangeClosed(1, 10)
.reduce(1, (a, b) -> a * b);
System.out.println("\n10! = " + factorial);
// Primitive stream with filter
long evenNumbers = IntStream.rangeClosed(1, 20)
.filter(n -> n % 2 == 0)
.count();
System.out.println("Even numbers 1-20: " + evenNumbers);
// MapToObj for transformation
List<String> numbersAsStrings = IntStream.rangeClosed(1, 5)
.mapToObj(n -> "Number " + n)
.collect(Collectors.toList());
System.out.println("Numbers as strings: " + numbersAsStrings);
}
// Product class
static class Product {
private String name;
private String category;
private double price;
private int stock;
public Product(String name, String category, double price, int stock) {
this.name = name;
this.category = category;
this.price = price;
this.stock = stock;
}
public String getName() { return name; }
public String getCategory() { return category; }
public double getPrice() { return price; }
public int getStock() { return stock; }
}
}
Functional Interfaces Overview
| Interface | Method | Description | Example |
|---|---|---|---|
Predicate<T> | boolean test(T t) | Test condition | s -> s.length() > 5 |
Function<T,R> | R apply(T t) | Transform | s -> s.toUpperCase() |
Consumer<T> | void accept(T t) | Consume | System.out::println |
Supplier<T> | T get() | Supply | () -> new Random() |
UnaryOperator<T> | T apply(T t) | Unary operation | x -> x * x |
BinaryOperator<T> | T apply(T t1, T t2) | Binary operation | (a, b) -> a + b |
Stream Operations Overview
Intermediate Operations (Lazy)
// Filter
stream.filter(x -> x > 0)
// Map
stream.map(x -> x * 2)
// FlatMap
stream.flatMap(list -> list.stream())
// Sorted
stream.sorted()
stream.sorted(Comparator.reverseOrder())
// Distinct
stream.distinct()
// Limit/Skip
stream.limit(10)
stream.skip(5)
// Peek (for debugging)
stream.peek(System.out::println)
Terminal Operations (Eager)
// ForEach
stream.forEach(System.out::println)
// Collect
stream.collect(Collectors.toList())
// Reduce
stream.reduce((a, b) -> a + b)
// Count
stream.count()
// Min/Max
stream.min(Comparator.naturalOrder())
stream.max(Comparator.reverseOrder())
// Match
stream.anyMatch(x -> x > 0)
stream.allMatch(x -> x > 0)
stream.noneMatch(x -> x > 0)
// Find
stream.findFirst()
stream.findAny()
Method Reference Types
Static Method Reference
// Lambda: s -> Integer.parseInt(s)
// Method reference: Integer::parseInt
list.stream().map(Integer::parseInt)
Instance Method Reference
// Lambda: s -> s.toUpperCase()
// Method reference: String::toUpperCase
list.stream().map(String::toUpperCase)
Constructor Reference
// Lambda: name -> new Person(name)
// Method reference: Person::new
list.stream().map(Person::new)
Performance Considerations
When to Use Streams
- Complex data processing: Filter, map, reduce operations
- Readability: Declarative code instead of imperative loops
- Parallelization: Easy conversion to parallel processing
- Functional programming: Immutable data structures
When to Avoid Streams
- Simple operations: Traditional loops are often faster
- Performance-critical code: Stream overhead can matter
- Primitive arrays: Specialized operations often work better
- Very small collections: Overhead outweighs benefits
Advantages and Disadvantages
Advantages of Stream API
- Readability: Declarative, expressive syntax
- Composability: Easy method chaining
- Parallelization: Simple conversion to parallel processing
- Functional: Strong support for functional programming patterns
- Lazy evaluation: Efficient processing
Disadvantages
- Performance: Overhead on simple operations
- Debugging: More difficult than imperative loops
- Learning curve: New concepts and syntax to master
- Memory: Intermediate collections can consume memory
Common Exam Questions
-
What’s the difference between intermediate and terminal operations? Intermediate operations are lazy and return a stream; terminal operations are eager and end the processing pipeline.
-
Explain lambda expressions. Anonymous functions with concise syntax:
(parameter) -> expressionor(parameter) -> { statements }. -
When should you use method references? As a shorter alternative to lambdas when an existing method matches your needs exactly.
-
What’s the benefit of parallel streams? Automatic parallel processing on multi-core systems for improved performance.
Key Resources
- https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
- https://docs.oracle.com/javase/tutorial/collections/streams/
- https://www.baeldung.com/java-8-streams



