Lambda-Ausdrücke
Ein Lambda ist eine kurze, namenlose Funktion. Sie passt überall, wo ein funktionales Interface (genau eine abstrakte Methode) erwartet wird:
import java.util.*;
import java.util.function.*;
public class Lambdas {
public static void main(String[] args) {
Function<Integer, Integer> quadrat = x -> x * x;
BiFunction<Integer, Integer, Integer> plus = (a, b) -> a + b;
Predicate<String> leer = s -> s.isEmpty();
Supplier<String> lieferant = () -> "Wert";
Consumer<String> ausgabe = s -> System.out.println("> " + s);
UnaryOperator<String> gross = String::toUpperCase; // Methodenreferenz
System.out.println(quadrat.apply(7) + " " + plus.apply(3, 4) + " " + leer.test("") + " " + lieferant.get());
ausgabe.accept(gross.apply("hallo"));
System.out.println(quadrat.andThen(x -> x + 1).apply(5)); // verketten
System.out.println(leer.negate().test("x"));
List<String> namen = new ArrayList<>(List.of("Zoe", "Anna", "Tom"));
namen.sort((a, b) -> a.length() - b.length());
namen.forEach(n -> System.out.print(n + " "));
System.out.println();
}
}49 7 true Wert > HALLO 26 true Zoe Tom Anna
| Interface | Methode | Bedeutung |
|---|---|---|
Function<T, R> | R apply(T) | wandelt um |
Predicate<T> | boolean test(T) | prüft |
Consumer<T> | void accept(T) | verarbeitet |
Supplier<T> | T get() | liefert |
BiFunction<T, U, R> | R apply(T, U) | zwei Eingaben |
UnaryOperator<T>, BinaryOperator<T> | gleiche Typen | |
Runnable, Callable<V> | run(), call() | Aufgaben |
Methodenreferenzen sind Kurzformen: String::length, System.out::println, Person::new, this::methode.
Streams
Ein Stream beschreibt eine Pipeline für Daten: Quelle → Zwischenoperationen → Endoperation. Das Original bleibt unverändert.
import java.util.*;
import java.util.stream.*;
public class Pipeline {
record Person(String name, int alter, String stadt) {}
public static void main(String[] args) {
List<Person> leute = List.of(
new Person("Mia", 17, "Köln"), new Person("Tom", 19, "Berlin"),
new Person("Zoe", 21, "Köln"), new Person("Ben", 15, "Bonn"));
List<String> volljaehrig = leute.stream()
.filter(p -> p.alter() >= 18)
.map(Person::name)
.sorted()
.collect(Collectors.toList());
System.out.println(volljaehrig);
double schnitt = leute.stream().mapToInt(Person::alter).average().orElse(0);
System.out.println(schnitt + " " + leute.stream().mapToInt(Person::alter).max().getAsInt());
System.out.println(leute.stream().anyMatch(p -> p.stadt().equals("Bonn")) + " " +
leute.stream().allMatch(p -> p.alter() > 16) + " " +
leute.stream().filter(p -> p.alter() > 100).findFirst().isPresent());
System.out.println(IntStream.rangeClosed(1, 5).boxed().collect(Collectors.toList()));
System.out.println(Stream.of("a", "b", "c").collect(Collectors.joining(", ", "[", "]")));
}
}[Tom, Zoe] 18.0 21 true false false [1, 2, 3, 4, 5] [a, b, c]
Typische Operationen
| Zwischenoperation | Wirkung |
|---|---|
filter(p) | behält passende Elemente |
map(f) | wandelt jedes Element um |
flatMap(f) | wandelt und flacht ein |
sorted(), sorted(cmp) | sortiert |
distinct() | entfernt Duplikate |
limit(n), skip(n) | begrenzt / überspringt |
peek(c) | Blick hinein (zum Debuggen) |
| Endoperation | Wirkung |
|---|---|
collect(...) | sammelt in Liste, Set, Map, ... |
forEach(c) | wendet an |
count(), sum(), average(), min, max | Auswertungen |
reduce(init, op) | faltet zu einem Wert |
anyMatch, allMatch, noneMatch | prüfen |
findFirst(), findAny() | ein Element (Optional) |
toList() | unveränderliche Liste (ab Java 16) |
Gruppieren und Zusammenfassen
Collectors bietet mächtige Auswertungen:
import java.util.*;
import java.util.stream.*;
public class Gruppen {
record Person(String name, int alter, String stadt) {}
public static void main(String[] args) {
List<Person> leute = List.of(
new Person("Mia", 17, "Köln"), new Person("Tom", 19, "Berlin"),
new Person("Zoe", 21, "Köln"), new Person("Ben", 15, "Bonn"));
Map<String, List<String>> nachStadt = leute.stream()
.collect(Collectors.groupingBy(Person::stadt, TreeMap::new, Collectors.mapping(Person::name, Collectors.toList())));
System.out.println(nachStadt);
Map<String, Long> anzahl = leute.stream().collect(Collectors.groupingBy(Person::stadt, TreeMap::new, Collectors.counting()));
System.out.println(anzahl);
Map<Boolean, List<String>> geteilt = leute.stream()
.collect(Collectors.partitioningBy(p -> p.alter() >= 18, Collectors.mapping(Person::name, Collectors.toList())));
System.out.println(geteilt);
Map<String, Integer> alterMap = leute.stream().collect(Collectors.toMap(Person::name, Person::alter));
System.out.println(new TreeMap<>(alterMap));
IntSummaryStatistics st = leute.stream().mapToInt(Person::alter).summaryStatistics();
System.out.println(st.getMin() + " " + st.getMax() + " " + st.getAverage() + " " + st.getSum());
System.out.println(leute.stream().map(Person::alter).reduce(0, Integer::sum));
System.out.println(leute.stream().sorted(Comparator.comparingInt(Person::alter).reversed()).map(Person::name).toList());
}
}{Berlin=[Tom], Bonn=[Ben], K?ln=[Mia, Zoe]}
{Berlin=1, Bonn=1, K?ln=2}
{false=[Mia, Ben], true=[Tom, Zoe]}
{Ben=15, Mia=17, Tom=19, Zoe=21}
15 21 18.0 72
72
[Zoe, Tom, Mia, Ben]Streams sind lazy
Zwischenoperationen laufen erst, wenn eine Endoperation sie anstößt. Das erlaubt auch unendliche Streams:
import java.util.stream.*;
public class Faul {
public static void main(String[] args) {
var ergebnis = Stream.iterate(1, n -> n * 2) // 1, 2, 4, 8, ... unendlich
.peek(n -> System.out.print("[" + n + "]"))
.filter(n -> n > 10)
.limit(2)
.toList();
System.out.println();
System.out.println(ergebnis);
System.out.println(IntStream.range(0, 20).filter(n -> n % 3 == 0).sum());
}
}[1][2][4][8][16][32] [16, 32] 63
Parallele Streams
list.parallelStream() verteilt die Arbeit auf mehrere Kerne. Sinnvoll nur bei vielen Elementen und rechenintensiven, nebenwirkungsfreien Schritten. Messen, bevor du es einsetzt.
Merke
- Lambdas
(a, b) -> a + bund MethodenreferenzenString::lengthersetzen kleine Klassen - Funktionale Interfaces:
Function,Predicate,Consumer,Supplier - Stream = Quelle →
filter/map/sorted→ Endoperation (collect,count,reduce) Collectors.groupingBy,partitioningBy,toMap,joiningfür Auswertungen- Streams sind lazy und einmal verwendbar
Aufgabe
Gegeben eine Liste von Bestellungen (Kunde, Betrag): Berechne mit Streams den Umsatz je Kunde und gib die Top 3 aus.