If you're tired of writing clunky for-loops to manipulate collections in Java, then the Streams API is your best friend. Introduced in Java 8, the Stream API brings functional programming to Java, making your code more expressive, readable, and concise.
In this blog post, we’ll break down what streams are, how they work, and explore real-world examples to get you comfortable with this powerful tool.
What is a Stream?
A Stream in Java represents a sequence of elements supporting sequential and parallel aggregate operations. Think of it as a pipeline of data where you can apply a series of transformations and computations in a fluent, functional style.
⚠️ Streams don’t store data. They simply convey elements from a source (like a List) through a pipeline of operations.
Stream Operations: The Basics
Stream operations are typically chained and categorized into two types:
Intermediate Operations – Return another stream (e.g., map, filter, sorted)
Terminal Operations – Produce a result or side-effect (e.g., collect, forEach, reduce)
Here’s the general syntax:
streamSource
.intermediateOperation()
.intermediateOperation()
.terminalOperation();
Practical Examples
1. Filtering a List
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filtered = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println(filtered); // Output: [Alice]
2. Mapping Elements (Transforming)
List<String> lowerCase = Arrays.asList("apple", "banana");
List<String> upperCase = lowerCase.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCase); // Output: [APPLE, BANANA]
3. Sorting Elements
List<Integer> nums = Arrays.asList(5, 3, 8, 1);
List<Integer> sorted = nums.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sorted); // Output: [1, 3, 5, 8]
4. Reducing a List to a Single Value
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
int sum = nums.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // Output: 10
5. Finding the First Element
Optional<String> first = Stream.of("cat", "dog", "elephant")
.findFirst();
first.ifPresent(System.out::println); // Output: cat
6. Counting Elements
long count = Stream.of("a", "b", "a", "c")
.filter(s -> s.equals("a"))
.count();
System.out.println(count); // Output: 2
7. Working with Custom Objects
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name; this.price = price;
}
}
List<Product> products = Arrays.asList(
new Product("Laptop", 999.99),
new Product("Phone", 499.99),
new Product("Tablet", 299.99)
);
List<String> expensiveProducts = products.stream()
.filter(p -> p.price > 300)
.map(p -> p.name)
.collect(Collectors.toList());
System.out.println(expensiveProducts); // Output: [Laptop, Phone]
Bonus: Parallel Streams
Want to speed up operations on large datasets? Use parallelStream:
List<Integer> bigList = IntStream.rangeClosed(1, 1_000_000)
.boxed()
.collect(Collectors.toList());
long sum = bigList.parallelStream()
.mapToLong(Integer::longValue)
.sum();
System.out.println(sum); // Output: 500000500000
Top comments (0)