filter() and map() in Java Streams
Fast recall
Quick revision
Core answer
Understanding of the two most fundamental stream operations and their distinct purposes in data processing pipelines.
Confusing filter (selection based on condition) with map (transformation of values), or thinking they modify the original collection.
Demonstrate a realistic chain using both operations together, like filtering active users then mapping to their email addresses for notifications.
Interview-ready guide
Interview answer
filter is selection: its Predicate decides whether each received element continues. map is transformation: its Function produces one result for each element that reaches it, and the result type may differ.
For example, a pipeline can filter unpaid orders and then map those orders to customer email addresses. Both stages are lazy, so they run only as a terminal operation requests data; findFirst may stop before every order is visited. Returning null from map does not remove an element—it emits a null value. Use filter for removal, and keep stream functions non-interfering rather than modifying the source during traversal.
filter() and map() in Java Streams
Direct Answer
filter() keeps only elements that match a given condition using a Predicate, while map() transforms each element into a new value using a Function. Both are intermediate operations that return a new Stream and are lazily evaluated until a terminal operation is called.
Code Example
record Order(Long id, String customerId, double amount, boolean isPaid) {}
class OrderService {
public List<String> getUnpaidHighValueCustomers(List<Order> orders) {
return orders.stream()
.filter(order -> !order.isPaid()) // Keep only unpaid orders
.filter(order -> order.amount() > 1000.0) // Keep only high-value orders
.map(Order::customerId) // Transform Order to customerId
.distinct() // Remove duplicate customers
.collect(Collectors.toList()); // Terminal operation
}
public List<String> formatActiveOrderSummaries(List<Order> orders) {
return orders.stream()
.filter(Order::isPaid) // Selection: only paid orders
.map(order -> String.format( // Transformation: Order to String
"Order #%d: $%.2f",
order.id(),
order.amount()
))
.collect(Collectors.toList());
}
}
Common Mistakes & Pitfalls
- Using
map()to remove elements: returning null creates a null stream element. Usefilter()for a selection decision. - Putting source mutation in a predicate or mapper: behavioral parameters should not interfere with the source and should normally avoid shared state.
- Assuming every source element is visited: short-circuit terminals such as
findFirstmay stop before later elements reach either stage. - Reusing the pipeline for another result: a terminal operation consumes it. Create a fresh stream, collect once and reuse the result, or use an appropriate collector;
Collectors.teeingis a later Java 12 API, not a Java 8 example.
Check your understanding
Answer these without looking back at the article.
- What is flatMap() and how does it differ from map()?
- When should you filter before mapping vs after?
- Can you use map() to return null values?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.