A lambda is a short anonymous function: s -> s.score() >= 30. Lambdas implement functional interfaces — interfaces with one abstract method such as Predicate<T>, Function<T, R>, Consumer<T> and Supplier<T>. Method references (Student::name) are shorthand for lambdas that just call a method.
Streams process collections declaratively: filter keeps items, map transforms them, sorted orders them, and terminal operations such as toList(), count(), average() and collect(...) produce a result. Collectors.groupingBy builds maps of groups in one step.
Optional<T> represents a value that may be absent — returned by findFirst, max and similar. Use orElse, map and ifPresent instead of returning null.
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Streams {
record Result(String student, int form, String subject, int score) {}
public static void main(String[] args) {
List<Result> results = List.of(
new Result("Amina", 4, "Maths", 88), new Result("Amina", 4, "Biology", 79),
new Result("Juma", 4, "Maths", 29), new Result("Juma", 4, "Biology", 48),
new Result("Neema", 4, "Maths", 71), new Result("Neema", 4, "Biology", 84),
new Result("Ali", 3, "Maths", 95), new Result("Ali", 3, "Biology", 62)
);
Predicate<Result> passed = r -> r.score() >= 30;
List<String> topMaths = results.stream()
.filter(r -> r.subject().equals("Maths"))
.sorted(Comparator.comparingInt(Result::score).reversed())
.limit(3)
.map(Result::student)
.toList();
System.out.println("Top Maths: " + topMaths);
Map<String, Double> averageByStudent = results.stream()
.collect(Collectors.groupingBy(Result::student, TreeMap::new,
Collectors.averagingInt(Result::score)));
System.out.println(averageByStudent);
Map<Boolean, Long> passFail = results.stream()
.collect(Collectors.partitioningBy(passed, Collectors.counting()));
System.out.println("Passed: " + passFail.get(true) + ", failed: " + passFail.get(false));
IntSummaryStatistics stats = results.stream().mapToInt(Result::score).summaryStatistics();
System.out.printf("min %d, max %d, avg %.1f%n", stats.getMin(), stats.getMax(), stats.getAverage());
Optional<Result> firstFail = results.stream().filter(passed.negate()).findFirst();
firstFail.ifPresent(r -> System.out.println("First fail: " + r.student() + " in " + r.subject()));
System.out.println(results.stream().filter(r -> r.score() > 100).findAny()
.map(Result::student).orElse("no impossible scores"));
String csv = results.stream()
.map(r -> r.student() + ":" + r.score())
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(csv);
}
}Key points
- Lambdas and method references implement functional interfaces concisely.
- Stream pipelines: source → filter/map/sorted → terminal operation.
- Return
Optionalinstead ofnullfor values that may be missing.
Exercise
From the results list, use streams to find: each subject's highest scorer, students who passed every subject, and a Map<String, List<Integer>> of student → scores sorted descending.
Show solution
Try the exercise yourself first — then compare your approach with this one.
groupingBy with maxBy finds each subject's top result. Students who passed everything are those whose minimum score is at least 30. The last map groups each student's scores into a list and sorts it in descending order with collectingAndThen.
import java.util.*;
import java.util.stream.Collectors;
public class StreamReports {
record Result(String student, String subject, int score) {}
public static void main(String[] args) {
List<Result> results = List.of(
new Result("Amina", "Maths", 88), new Result("Amina", "Biology", 79),
new Result("Juma", "Maths", 29), new Result("Juma", "Biology", 48),
new Result("Neema", "Maths", 71), new Result("Neema", "Biology", 84),
new Result("Ali", "Maths", 95), new Result("Ali", "Biology", 62));
Map<String, String> topPerSubject = results.stream()
.collect(Collectors.groupingBy(Result::subject, TreeMap::new,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparingInt(Result::score)),
best -> best.map(Result::student).orElse("-"))));
System.out.println(topPerSubject); // {Biology=Neema, Maths=Ali}
List<String> passedAll = results.stream()
.collect(Collectors.groupingBy(Result::student, TreeMap::new,
Collectors.minBy(Comparator.comparingInt(Result::score))))
.entrySet().stream()
.filter(e -> e.getValue().map(r -> r.score() >= 30).orElse(false))
.map(Map.Entry::getKey)
.toList();
System.out.println(passedAll); // [Ali, Amina, Neema]
Map<String, List<Integer>> scores = results.stream()
.collect(Collectors.groupingBy(Result::student, TreeMap::new,
Collectors.mapping(Result::score, Collectors.collectingAndThen(Collectors.toList(), list -> {
list.sort(Comparator.reverseOrder());
return list;
}))));
System.out.println(scores); // {Ali=[95, 62], Amina=[88, 79], Juma=[48, 29], Neema=[84, 71]}
}
}