Stream Filter, Map and Reduce
filter, map and reduce are the three most important stream operations. filter is used to keep only the elements that pass a test. map is used to transform each element into another value. reduce is used to combine all elements into a single result, such as a total. Almost every stream pipeline uses at least one of them. In this lesson, you will learn each operation with examples, and then use all three together.
Declaration
Stream<T> filter(Predicate<? super T> predicate)
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
T reduce(T identity, BinaryOperator<T> accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator)filter: Keep Matching Elements
filter takes a Predicate and keeps each element for which it returns true.
import java.util.List;
public class FilterDemo {
public static void main(String[] args) {
List<Integer> marks = List.of(91, 38, 85, 42, 77);
System.out.println(marks.stream().filter(m -> m >= 50).toList());
List<String> names =
List.of("Navika", "Srihitha", "Advitha", "Anamaneni");
System.out.println(names.stream()
.filter(n -> n.startsWith("A"))
.filter(n -> n.length() < 9)
.toList());
}
}[91, 85, 77]
[Advitha]Explanation: The first filter keeps marks of 50 or more. Two filter calls in a row act like "and": the names must start with A and be shorter than 9 letters.
map: Transform Each Element
map takes a Function and replaces each element with the function's result. The type can change, for example from String to Integer.
import java.util.List;
public class MapDemo {
public static void main(String[] args) {
List<String> names = List.of("navika", "srihitha", "advitha");
System.out.println(names.stream().map(String::toUpperCase).toList());
System.out.println(names.stream().map(String::length).toList());
System.out.println(names.stream()
.map(n -> n.substring(0, 1).toUpperCase() + n.substring(1))
.toList());
}
}[NAVIKA, SRIHITHA, ADVITHA]
[6, 8, 7]
[Navika, Srihitha, Advitha]Explanation: The first map changes each name to upper case. The second changes each String into an Integer (its length). The third capitalizes the first letter.
reduce: Combine into One Value
reduce combines elements two at a time. With an identity (a starting value), it returns a value; without one, it returns an Optional, because the stream might be empty.
import java.util.List;
import java.util.Optional;
public class ReduceDemo {
public static void main(String[] args) {
List<Integer> marks = List.of(91, 78, 85);
int total = marks.stream().reduce(0, (a, b) -> a + b);
System.out.println("Total: " + total);
int product = marks.stream().reduce(1, (a, b) -> a * b);
System.out.println("Product: " + product);
Optional<Integer> max = marks.stream().reduce(Integer::max);
System.out.println("Max: " + max.get());
String joined = List.of("Navika", "Srihitha").stream()
.reduce("", (a, b) -> a + b.charAt(0));
System.out.println("Initials: " + joined);
}
}Total: 254
Product: 603330
Max: 91
Initials: NSExplanation: For the total, reduce starts at 0 and adds each mark: 0 + 91, then 91 + 78, then 169 + 85. The product starts at 1. reduce(Integer::max) has no identity, so it returns an Optional. The last example builds a string of first letters.
Example: All Three Together
import java.util.List;
public class AllThree {
record Student(String name, int marks) { }
public static void main(String[] args) {
List<Student> students = List.of(
new Student("Navika", 91), new Student("Srihitha", 38),
new Student("Advitha", 85), new Student("Anamaneni", 72));
int passedTotal = students.stream()
.filter(s -> s.marks() >= 50)
.map(Student::marks)
.reduce(0, Integer::sum);
System.out.println("Total marks of those who passed: " + passedTotal);
}
}Total marks of those who passed: 248Explanation: filter keeps the three students who passed, map turns each into their marks, and reduce adds them: 91 + 85 + 72 = 248.
Common Mistakes
Using a wrong identity, such as reduce(1, Integer::sum). The identity must not change the result: 0 for addition, 1 for multiplication, "" for joining.
Calling get() on reduce without an identity on a possibly empty stream. Use orElse.
Using map when the function returns a stream. That gives a stream of streams; use flatMap.
Interview Corner
What is the difference between map and filter?
filter takes a Predicate and keeps only the elements that satisfy it, so it can reduce the number of elements but doesn't change them. map takes a Function and transforms each element into a new value, possibly of a different type, so the number of elements stays the same.
Practice
- Easy: From a list of prices, keep those under 500, add 18% tax to each, and print the new list.
- Harder: Using
reduce, find the longest name in a list of names (if two have the same length, keep the first).
Key Takeaways
Was this tutorial helpful?