Streams API
Overview
The Streams API revolutionized how Java developers process collections of data. Instead of writing verbose `for` loops to filter, sort, and transform lists, Streams allow you to write declarative, pipeline-style code. You declare *what* you want to achieve rather than *how* to loop through it. Streams process data sequentially or in parallel, utilizing functional operations like `map()`, `filter()`, and `reduce()`.
Syntax
A Stream pipeline consists of a Source (the list), Intermediate Operations (filter, map - which return a new stream and are lazy), and a Terminal Operation (collect, count, forEach - which triggers the actual processing).
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Goal: Find the square of all even numbers
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // Intermediate Op: Keeps only evens (2, 4, 6...)
.map(n -> n * n) // Intermediate Op: Squares them (4, 16, 36...)
.collect(Collectors.toList()); // Terminal Op: Gathers them back into a List
System.out.println(evenSquares); // [4, 16, 36, 64, 100]Streams abstract away the iteration logic, making data aggregations, groupings, and reductions incredibly intuitive.
List<User> users = fetchUsers();
// 1. Grouping Data
Map<String, List<User>> usersByRole = users.stream()
.collect(Collectors.groupingBy(User::getRole));
// 2. Finding an Element
Optional<User> firstAdmin = users.stream()
.filter(u -> "ADMIN".equals(u.getRole()))
.findFirst();
// 3. Aggregation (Summing)
double totalSalaries = users.stream()
.mapToDouble(User::getSalary)
.sum();Common Pitfalls
- Assuming Streams are faster than standard for-loops. For simple iteration on small lists, for-loops are actually faster. Streams excel at complex transformations and large datasets.
- Reusing a stream. A Stream can only be traversed ONCE. Once a terminal operation executes, the stream is closed.
Interview Tips
- Distinguish between Intermediate operations (lazy, return a stream) and Terminal operations (trigger execution, return a result).
Real-World Example
Processing a massive dataset of E-Commerce transactions to find the top 5 highest-spending users in the past week.
public class ReportGenerator {
public List<String> getTopSpenders(List<Transaction> transactions) {
return transactions.stream()
.filter(t -> t.getDate().isAfter(LocalDate.now().minusDays(7)))
.filter(t -> t.getStatus() == Status.COMPLETED)
.sorted(Comparator.comparing(Transaction::getAmount).reversed())
.limit(5)
.map(Transaction::getCustomerName)
.collect(Collectors.toList());
}
}