Lambdas and streams
Lambdas
A lambda is a short, unnamed function that you can pass around like a value:
Java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("Asha", "Leo", "Mei", "Carlos", "Bea"));
names.forEach(name -> System.out.println("Hi, " + name));
names.removeIf(name -> name.length() <= 3);
System.out.println(names); // [Asha, Carlos]
names.sort((a, b) -> b.compareTo(a));
System.out.println(names); // [Carlos, Asha]
}
}name -> ...takes one parameter,(a, b) -> ...takes two, and() -> ...takes none.- For more than one statement, use braces and
return:(a, b) -> { int d = a - b; return d; }.
Functional interfaces
A lambda can go wherever Java expects an interface with exactly one abstract method. The java.util.function package has the common shapes:
Java
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) {
Predicate<Integer> isEven = n -> n % 2 == 0;
Function<String, Integer> length = s -> s.length();
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Supplier<String> greeting = () -> "Hello!";
System.out.println(isEven.test(10)); // true
System.out.println(length.apply("CodeMIRAI")); // 9
System.out.println(add.apply(3, 4)); // 7
System.out.println(greeting.get()); // Hello!
}
}| Interface | Takes | Returns | Method |
|---|---|---|---|
Predicate<T> | a T | a boolean | test |
Function<T, R> | a T | an R | apply |
Consumer<T> | a T | nothing | accept |
Supplier<T> | nothing | a T | get |
Method references
When a lambda only calls an existing method, a method reference says the same thing more briefly:
Java
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println); // the same
names.sort((a, b) -> a.compareToIgnoreCase(b));
names.sort(String::compareToIgnoreCase); // the sameStreams
A stream processes a collection as a pipeline of steps:
Java
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> nums = List.of(5, 12, 8, 3, 20, 15, 7);
List<Integer> result = nums.stream()
.filter(n -> n > 6) // keep 12, 8, 20, 15, 7
.map(n -> n * 10) // 120, 80, 200, 150, 70
.sorted() // 70, 80, 120, 150, 200
.collect(Collectors.toList());
System.out.println(result);
int sum = nums.stream().mapToInt(Integer::intValue).sum();
double avg = nums.stream().mapToInt(Integer::intValue).average().orElse(0);
long big = nums.stream().filter(n -> n >= 10).count();
System.out.println("sum " + sum + ", average " + avg + ", 10 or more: " + big);
}
}filterkeeps the items that pass a test, andmaptransforms each item.sortedsorts, andcollectgathers the results into a new collection. (Java 16 added a shorter.toList(), but the code runner uses Java 15.)- Nothing happens until a final step such as
collect,countorsumasks for a result, and the original list is never changed.
Streams of objects
Java
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
class Student {
private final String name;
private final String course;
private final int score;
Student(String name, String course, int score) {
this.name = name;
this.course = course;
this.score = score;
}
String getName() { return name; }
String getCourse() { return course; }
int getScore() { return score; }
}
public class Main {
public static void main(String[] args) {
List<Student> students = List.of(
new Student("Asha", "Java", 92),
new Student("Leo", "Python", 78),
new Student("Mei", "Java", 85),
new Student("Raj", "Python", 88),
new Student("Zoe", "SQL", 95)
);
String topThree = students.stream()
.sorted(Comparator.comparingInt(Student::getScore).reversed())
.limit(3)
.map(Student::getName)
.collect(Collectors.joining(", "));
System.out.println("Top three: " + topThree);
Map<String, Double> averageByCourse = students.stream()
.collect(Collectors.groupingBy(Student::getCourse, TreeMap::new,
Collectors.averagingInt(Student::getScore)));
System.out.println(averageByCourse);
boolean anyBelow50 = students.stream().anyMatch(s -> s.getScore() < 50);
System.out.println("Anyone below 50? " + anyBelow50);
}
}groupingBy sorts items into groups, here one per course, and averagingInt reduces each group to an average. A TreeMap keeps the courses in alphabetical order.
Loop or stream?
Streams shine for filter-transform-collect pipelines over data. A plain loop is often clearer when you need indexes, an early exit, or several variables changing together. Both are good Java: choose whichever reads better.
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.