Java Stream API: Lambda, Functional Interfaces, Map, Filter, Reduce & Collect
This post is a comprehensive guide to the Java Stream API – including Lambda expressions, Functional Interfaces, map, filter, reduce and collect with practical examples.
In a Nutshell
Java Stream API enables functional data processing with Lambda expressions. map transforms, filter selects, reduce aggregates and collect gathers results in containers.
Compact Technical Description
Java Stream API is a functional API for processing data collections. It supports declarative programming with Lambda expressions and Functional Interfaces.
Important Concepts:
Lambda Expressions
- Syntax:
(parameter) -> expressionor(parameter) -> { statements } - Type Inference: 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) - Testing
- **Function<T,R>**: R apply(T t) - Transformation
- **Consumer<T>**: void accept(T t) - Consumption
- **Supplier<T>**: T get() - Generation
- **UnaryOperator<T>**: T apply(T t) - Unary operation
- **BinaryOperator<T>**: T apply(T t1, T t2) - Binary operation
```java
### Stream Operations
- **Intermediate**: map, filter, sorted, distinct, limit, skip
- **Terminal**: forEach, collect, reduce, count, anyMatch, allMatch
- **Short-circuiting**: findFirst, findAny, anyMatch, allMatch, noneMatch
## Exam-Relevant Key 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**: Transformation of elements
- **Filter**: Selection based on predicates
- **Reduce**: Aggregation of stream elements
- **Collect**: Gathering results in containers
- **IHK-relevant**: Modern Java, functional programming
## Core Components
1. **Lambda Expressions**: Compact function literals
2. **Functional Interfaces**: Typed function definitions
3. **Stream Creation**: Collections, Arrays, I/O, Generators
4. **Intermediate Operations**: Lazy, chainable operations
5. **Terminal Operations**: Eager, terminate stream processing
6. **Collectors**: Specialized collection operations
7. **Parallel Streams**: Parallel data processing
8. **Optional**: Null-safe container for values
## Practical Examples
### 1. Basic Stream Operations
```java
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 Basics ===");
// 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 filter
List<String> langeNamen = namen.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
System.out.println("Names with > 4 characters: " + 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("Even numbers > 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("Names starting with 'A' and ≤5 characters: " + 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("Name lengths: " + namenslaengen);
// Integer to String (squares)
List<String> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.map(quad -> "Square: " + quad)
.collect(Collectors.toList());
System.out.println("Squares: " + 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("Flattened: " + 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("Person 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("Sum: " + summe.orElse(0));
// Product with Reduce
Optional<Integer> produkt = zahlen.stream()
.reduce((a, b) -> a * b);
System.out.println("Product: " + 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("Sum with identity: " + summeMitIdentitaet);
// String concatenation
List<String> woerter = Arrays.asList("Java", "Stream", "API");
Optional<String> verkettet = woerter.stream()
.reduce((a, b) -> a + " " + b);
System.out.println("Concatenated: " + 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("Uppercase: " + grossgeschrieben);
// To Set
Set<Integer> quadrate = zahlen.stream()
.map(zahl -> zahl * zahl)
.collect(Collectors.toSet());
System.out.println("Squares as 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("Names map: " + namenMap);
// Grouping By
Map<Integer, List<String>> nachLaengeGruppiert = namen.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println("Grouped by length: " + nachLaengeGruppiert);
// Partitioning By
Map<Boolean, List<Integer>> geradeUngerade = zahlen.stream()
.collect(Collectors.partitioningBy(zahl -> zahl % 2 == 0));
System.out.println("Partitioned: " + geradeUngerade);
// Joining
String namensliste = namen.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println("Names list: " + namensliste);
// Summarizing
IntSummaryStatistics statistik = zahlen.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("Statistics: " + statistik);
}
private static void methodenreferenzenDemo() {
System.out.println("\n--- Method References 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("Static reference: " + grossgeschrieben);
// Instance method reference
List<Integer> laengen = namen.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("Instance reference: " + laengen);
// Constructor reference
List<Person> personen = namen.stream()
.map(name -> new Person(name, 20 + name.length()))
.collect(Collectors.toList());
System.out.println("Constructor reference: " +
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 FortgeschritteneStreams {
public static void main(String[] args) {
System.out.println("=== Advanced Stream Operations ===");
// Data for demonstrations
List<Student> studenten = Arrays.asList(
new Student("Alice", "Informatik", 85, 3),
new Student("Bob", "Mathematik", 92, 2),
new Student("Charlie", "Informatik", 78, 4),
new Student("Diana", "Physik", 88, 1),
new Student("Eve", "Informatik", 95, 2),
new Student("Frank", "Mathematik", 73, 3)
);
// Sorting
sortierungDemo(studenten);
// Limit and Skip
limitSkipDemo(studenten);
// Distinct
distinctDemo();
// Match operations
matchDemo(studenten);
// Find operations
findDemo(studenten);
// Optional handling
optionalDemo(studenten);
// Parallel Streams
parallelStreamDemo(studenten);
}
private static void sortierungDemo(List<Student> studenten) {
System.out.println("\n--- Sorting Demo ---");
// Sort by grade
List<Student> nachNote = studenten.stream()
.sorted(Comparator.comparing(Student::getNote))
.collect(Collectors.toList());
System.out.println("By grade ascending:");
nachNote.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getNote()));
// Sort by grade descending
List<Student> nachNoteAbsteigend = studenten.stream()
.sorted(Comparator.comparing(Student::getNote).reversed())
.collect(Collectors.toList());
System.out.println("\nBy grade descending:");
nachNoteAbsteigend.forEach(s -> System.out.println(" " + s.getName() + ": " + s.getNote()));
// Multi-criteria sorting
List<Student> mehrkriterium = studenten.stream()
.sorted(Comparator
.comparing(Student::getFach)
.thenComparing(Student::getNote)
.thenComparing(Student::getName))
.collect(Collectors.toList());
System.out.println("\nBy subject, grade, name:");
mehrkriterium.forEach(s -> System.out.println(" " + s.getFach() + " - " +
s.getName() + ": " + s.getNote()));
}
private static void limitSkipDemo(List<Student> studenten) {
System.out.println("\n--- Limit and Skip Demo ---");
// First 3 students
List<Student> ersteDrei = studenten.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println("First 3 students:");
ersteDrei.forEach(s -> System.out.println(" " + s.getName()));
// Skip the first 2
List<Student> nachUeberspringen = studenten.stream()
.skip(2)
.collect(Collectors.toList());
System.out.println("\nAfter skipping the first 2:");
nachUeberspringen.forEach(s -> System.out.println(" " + s.getName()));
// Pagination (Page 2, 2 elements per page)
int seite = 2;
int groesse = 2;
List<Student> paginiert = studenten.stream()
.skip((seite - 1) * groesse)
.limit(groesse)
.collect(Collectors.toList());
System.out.println("\nPage " + seite + " (size " + groesse + "):");
paginiert.forEach(s -> System.out.println(" " + s.getName()));
}
private static void distinctDemo() {
System.out.println("\n--- Distinct Demo ---");
List<Integer> zahlenMitDuplikaten = Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5, 1);
List<Integer> eindeutigeZahlen = zahlenMitDuplikaten.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("With duplicates: " + zahlenMitDuplikaten);
System.out.println("Unique: " + eindeutigeZahlen);
// Distinct with objects
List<String> faecher = Arrays.asList("Informatik", "Mathematik", "Informatik",
"Physik", "Mathematik", "Informatik");
List<String> eindeutigeFaecher = faecher.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("\nSubjects with duplicates: " + faecher);
System.out.println("Unique subjects: " + eindeutigeFaecher);
}
private static void matchDemo(List<Student> studenten) {
System.out.println("\n--- Match Demo ---");
// All Match - all meet condition
boolean alleBestanden = studenten.stream()
.allMatch(s -> s.getNote() >= 50);
System.out.println("All passed: " + alleBestanden);
boolean alleInformatik = studenten.stream()
.allMatch(s -> s.getFach().equals("Informatik"));
System.out.println("All Computer Science: " + alleInformatik);
// Any Match - at least one meets condition
boolean jemandInformatik = studenten.stream()
.anyMatch(s -> s.getFach().equals("Informatik"));
System.out.println("Someone Computer Science: " + jemandInformatik);
boolean jemandSehrGut = studenten.stream()
.anyMatch(s -> s.getNote() >= 90);
System.out.println("Someone excellent: " + jemandSehrGut);
// None Match - none meet condition
boolean niemandDurchgefallen = studenten.stream()
.noneMatch(s -> s.getNote() < 50);
System.out.println("No one failed: " + niemandDurchgefallen);
}
private static void findDemo(List<Student> studenten) {
System.out.println("\n--- Find Demo ---");
// Find First - first element
Optional<Student> erster = studenten.stream()
.findFirst();
erster.ifPresent(s -> System.out.println("First student: " + s.getName()));
// Find Any - any element (especially with parallel streams)
Optional<Student> irgendeinInformatiker = studenten.stream()
.filter(s -> s.getFach().equals("Informatik"))
.findAny();
irgendeinInformatiker.ifPresent(s ->
System.out.println("Any computer scientist: " + s.getName()));
// Find with complex predicate
Optional<Student> besterMathematiker = studenten.stream()
.filter(s -> s.getFach().equals("Mathematik"))
.max(Comparator.comparing(Student::getNote));
besterMathematiker.ifPresent(s ->
System.out.println("Best mathematician: " + s.getName() + " (" + s.getNote() + ")"));
}
private static void optionalDemo(List<Student> studenten) {
System.out.println("\n--- Optional Handling Demo ---");
// Optional with map
Optional<String> ersterName = studenten.stream()
.findFirst()
.map(Student::getName);
ersterName.ifPresent(name -> System.out.println("First name: " + name));
// Optional with filter
Optional<Student> besterStudent = studenten.stream()
.max(Comparator.comparing(Student::getNote));
String besterName = besterStudent
.filter(s -> s.getNote() >= 90)
.map(Student::getName)
.orElse("None with 90+ points");
System.out.println("Best student (90+): " + besterName);
// Optional chaining
Optional<String> fachDesBesten = studenten.stream()
.max(Comparator.comparing(Student::getNote))
.flatMap(s -> Optional.ofNullable(s.getFach()))
.map(String::toUpperCase);
fachDesBesten.ifPresent(fach ->
System.out.println("Subject of the best: " + fach));
// Optional with supplier
String defaultValue = studenten.stream()
.filter(s -> s.getName().equals("NichtExistent"))
.findFirst()
.map(Student::getName)
.orElseGet(() -> "Default-Student");
System.out.println("Default value: " + defaultValue);
}
private static void parallelStreamDemo(List<Student> studenten) {
System.out.println("\n--- Parallel Stream Demo ---");
// Parallel processing
long startZeit = System.currentTimeMillis();
List<String> namenParallel = studenten.parallelStream()
.filter(s -> s.getNote() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
long endZeit = System.currentTimeMillis();
System.out.println("Parallel result: " + namenParallel);
System.out.println("Parallel time: " + (endZeit - startZeit) + "ms");
// Comparison with sequential processing
startZeit = System.currentTimeMillis();
List<String> namenSequentiell = studenten.stream()
.filter(s -> s.getNote() > 80)
.map(Student::getName)
.sorted()
.collect(Collectors.toList());
endZeit = System.currentTimeMillis();
System.out.println("\nSequential result: " + namenSequentiell);
System.out.println("Sequential time: " + (endZeit - startZeit) + "ms");
// Thread info in parallel stream
System.out.println("\nThread info in parallel stream:");
studenten.parallelStream()
.forEach(s -> System.out.println(s.getName() + " on " +
Thread.currentThread().getName()));
}
// Student class
static class Student {
private String name;
private String fach;
private int note;
private int semester;
public Student(String name, String fach, int note, int semester) {
this.name = name;
this.fach = fach;
this.note = note;
this.semester = semester;
}
public String getName() { return name; }
public String getFach() { return fach; }
public int getNote() { return note; }
public int getSemester() { return semester; }
}
}
3. Specialized Collectors and Custom Operations
import java.util.*;
import java.util.stream.*;
import java.util.function.*;
public class SpezialisierteCollectors {
public static void main(String[] args) {
System.out.println("=== Spezialisierte Collectors Demo ===");
// Testdaten
List<Produkt> produkte = Arrays.asList(
new Produkt("Laptop", "Elektronik", 999.99, 5),
new Produkt("Maus", "Elektronik", 29.99, 15),
new Produkt("Tastatur", "Elektronik", 79.99, 8),
new Produkt("Buch", "Bücher", 19.99, 20),
new Produkt("Stift", "Büro", 2.99, 50),
new Produkt("Papier", "Büro", 9.99, 30)
);
// Gruppierung mit Aggregation
gruppierungMitAggregation(produkte);
// Multi-Level Gruppierung
multiLevelGruppierung(produkte);
// Custom Collector
customCollectorDemo();
// Downstream Collectors
downstreamCollectorsDemo(produkte);
// Primitive Streams
primitiveStreamsDemo();
}
private static void gruppierungMitAggregation(List<Produkt> produkte) {
System.out.println("\n--- Gruppierung mit Aggregation ---");
// Grouping by category with statistics
Map<String, DoubleSummaryStatistics> preisStatistik = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.summarizingDouble(Produkt::getPreis)
));
preisStatistik.forEach((kategorie, statistik) -> {
System.out.println(kategorie + ":");
System.out.println(" Average: " + statistik.getAverage());
System.out.println(" Minimum: " + statistik.getMin());
System.out.println(" Maximum: " + statistik.getMax());
System.out.println(" Sum: " + statistik.getSum());
});
// Grouping with mapping
Map<String, Set<String>> kategorieNamen = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.mapping(Produkt::getName, Collectors.toSet())
));
System.out.println("\nCategories with product names:");
kategorieNamen.forEach((kategorie, namen) ->
System.out.println(kategorie + ": " + namen));
// Grouping with filter
Map<String, List<Produkt>> teureProdukte = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.filtering(p -> p.getPreis() > 50, Collectors.toList())
));
System.out.println("\nExpensive products (>50€):");
teureProdukte.forEach((kategorie, produkteListe) -> {
if (!produkteListe.isEmpty()) {
System.out.println(kategorie + ": " +
produkteListe.stream().map(Produkt::getName).collect(Collectors.toList()));
}
});
}
private static void multiLevelGruppierung(List<Produkt> produkte) {
System.out.println("\n--- Multi-Level Grouping ---");
// Group products by price categories
Map<String, Map<String, List<Produkt>>> multiLevel = produkte.stream()
.collect(Collectors.groupingBy(
p -> p.getPreis() < 50 ? "Günstig" : "Teuer",
Collectors.groupingBy(Produkt::getKategorie)
));
System.out.println("Multi-level grouping:");
multiLevel.forEach((preisKategorie, kategorieMap) -> {
System.out.println(preisKategorie + ":");
kategorieMap.forEach((kategorie, produkteListe) -> {
System.out.println(" " + kategorie + ": " +
produkteListe.stream().map(Produkt::getName).collect(Collectors.toList()));
});
});
}
private static void customCollectorDemo() {
System.out.println("\n--- Custom Collector Demo ---");
List<String> woerter = Arrays.asList("Java", "Stream", "API", "Functional", "Programming");
// Custom collector for string concatenation with separator 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 ergebnis = woerter.stream().collect(customStringCollector);
System.out.println("Custom collector result: " + ergebnis);
// 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> zahlen = Arrays.asList(10, 20, 30, 40, 50);
double durchschnitt = zahlen.stream().collect(averageCollector);
System.out.println("Custom average: " + durchschnitt);
}
private static void downstreamCollectorsDemo(List<Produkt> produkte) {
System.out.println("\n--- Downstream Collectors Demo ---");
// GroupingBy with counting
Map<String, Long> anzahlProKategorie = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.counting()
));
System.out.println("Count per category:");
anzahlProKategorie.forEach((kategorie, anzahl) ->
System.out.println(kategorie + ": " + anzahl));
// GroupingBy with summing
Map<String, Integer> lagerProKategorie = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.summingInt(Produkt::getLagerbestand)
));
System.out.println("\nInventory per category:");
lagerProKategorie.forEach((kategorie, bestand) ->
System.out.println(kategorie + ": " + bestand));
// GroupingBy with maxBy
Map<String, Optional<Produkt>> teuerstesProKategorie = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.maxBy(Comparator.comparing(Produkt::getPreis))
));
System.out.println("\nMost expensive product per category:");
teuerstesProKategorie.forEach((kategorie, optional) ->
optional.ifPresent(p -> System.out.println(kategorie + ": " + p.getName())));
// CollectingAndThen for post-processed results
Map<String, String> kategorieInfo = produkte.stream()
.collect(Collectors.groupingBy(
Produkt::getKategorie,
Collectors.collectingAndThen(
Collectors.toList(),
liste -> liste.size() + " products, " +
String.format("%.2f€",
liste.stream().mapToDouble(Produkt::getPreis).average().orElse(0))
)
));
System.out.println("\nCategory info:");
kategorieInfo.forEach((kategorie, info) -> System.out.println(kategorie + ": " + info));
}
private static void primitiveStreamsDemo() {
System.out.println("\n--- Primitive Streams Demo ---");
// IntStream
IntStream zahlen = IntStream.range(1, 10);
int summe = zahlen.sum();
System.out.println("Sum 1-9: " + summe);
// With boxed to object stream
List<Integer> zahlenListe = IntStream.rangeClosed(1, 5)
.boxed()
.collect(Collectors.toList());
System.out.println("Numbers as list: " + zahlenListe);
// DoubleStream with calculations
double[] preise = {19.99, 29.99, 99.99, 149.99};
DoubleSummaryStatistics preisStatistik = Arrays.stream(preise)
.summaryStatistics();
System.out.println("\nPrice statistics:");
System.out.println(" Count: " + preisStatistik.getCount());
System.out.println(" Sum: " + preisStatistik.getSum());
System.out.println(" Average: " + preisStatistik.getAverage());
System.out.println(" Min: " + preisStatistik.getMin());
System.out.println(" Max: " + preisStatistik.getMax());
// LongStream for large numbers
long fakultaet = LongStream.rangeClosed(1, 10)
.reduce(1, (a, b) -> a * b);
System.out.println("\n10! = " + fakultaet);
// Primitive stream with filter
long geradeZahlen = IntStream.rangeClosed(1, 20)
.filter(n -> n % 2 == 0)
.count();
System.out.println("Even numbers 1-20: " + geradeZahlen);
// MapToObj for transformation
List<String> zahlenAlsStrings = IntStream.rangeClosed(1, 5)
.mapToObj(n -> "Number " + n)
.collect(Collectors.toList());
System.out.println("Numbers as strings: " + zahlenAlsStrings);
}
// Product class
static class Produkt {
private String name;
private String kategorie;
private double preis;
private int lagerbestand;
public Produkt(String name, String kategorie, double preis, int lagerbestand) {
this.name = name;
this.kategorie = kategorie;
this.preis = preis;
this.lagerbestand = lagerbestand;
}
public String getName() { return name; }
public String getKategorie() { return kategorie; }
public double getPreis() { return preis; }
public int getLagerbestand() { return lagerbestand; }
}
}
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: Simple switch 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 be relevant
- Primitive Arrays: Specialized operations often better
- Very Small Collections: Overhead outweighs benefits
Advantages and Disadvantages
Advantages of Stream API
- Readability: Declarative, expressive syntax
- Composition: Easily chainable operations
- Parallelization: Simple switch to parallel processing
- Functional: Support for functional programming
- Lazy Evaluation: Efficient processing
Disadvantages
- Performance: Overhead with simple operations
- Debugging: More difficult than imperative loops
- Learning Curve: New concepts and syntax
- Memory: Intermediate collections can consume memory
Common Exam Questions
-
What is the difference between intermediate and terminal operations? Intermediate operations are lazy and return a Stream, terminal operations are eager and end the processing.
-
Explain lambda expressions! Anonymous functions with compact syntax:
(parameter) -> expressionor(parameter) -> { statements }. -
When do you use method references? As a shorter alternative to lambda expressions when a method exists that matches exactly.
-
What is the advantage of parallel streams? Automatic parallel processing on multi-core systems for better performance.
Most Important Sources
- 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