Java 8 Stream API is a powerful feature that provides a functional programming approach to processing collections of data. It allows performing operations like filtering, mapping, reducing, sorting, and collecting data efficiently.
Key Features of Stream API
- Supports Functional Programming.
- Processes data declaratively (reduces boilerplate code).
- Uses internal iteration instead of external iteration (better performance).
- Supports parallel execution using parallel streams.
- Does not modify the original data structure (works on a new stream).
How to Create a Stream?
Streams can be created from various sources such as collections, arrays, I/O resources, and even generators.
1. Creating Stream from a List
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
List names = Arrays.asList("Alice", "Bob", "Charlie");
// Creating a stream from a List
Stream stream = names.stream();
// Using forEach to print elements
stream.forEach(System.out::println);
}
}
2. Creating Stream from an Array
import java.util.stream.Stream;
public class StreamFromArray {
public static void main(String[] args) {
String[] arr = {"Java", "Python", "C++"};
Stream stream = Stream.of(arr);
stream.forEach(System.out::println);
}
}
3. Creating Infinite Streams (Generate & Iterate)
import java.util.stream.Stream;
public class InfiniteStreamExample {
public static void main(String[] args) {
// Generating an infinite stream of random numbers
Stream randomNumbers = Stream.generate(Math::random).limit(5);
randomNumbers.forEach(System.out::println);
}
}
Stream API Operations
Stream operations are divided into two types:
- Intermediate Operations (Return a Stream)
- Terminal Operations (Return a value or perform an action)
1. Intermediate Operations
Method | Description |
---|---|
filter(Predicate<T>) | Filters elements based on a condition. |
map(Function<T, R>) | Transforms elements into another form. |
flatMap(Function<T, Stream<R>>) | Flattens nested structures. |
distinct() | Removes duplicate elements. |
sorted() | Sorts elements. |
peek(Consumer<T>) | Performs an operation without altering elements. |
limit(long n) | Limits stream size. |
skip(long n) | Skips first n elements. |
2. Terminal Operations
Method | Description |
---|---|
collect(Collector<T, A, R>) | Converts stream into a collection (List, Set, etc.). |
forEach(Consumer<T>) | Performs an action on each element. |
count() | Returns number of elements in the stream. |
reduce(BinaryOperator<T>) | Reduces stream to a single value. |
min(Comparator<T>) , max(Comparator<T>) | Finds minimum/maximum value. |
allMatch(Predicate<T>) , anyMatch(Predicate<T>) , noneMatch(Predicate<T>) | Checks conditions on stream elements. |
findFirst() | Returns first element. |
findAny() | Returns any element (useful for parallel streams). |
Stream API Examples
1. Filtering a List using filter()
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterExample {
public static void main(String[] args) {
List names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");
// Filtering names that start with 'A'
List filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println(filteredNames);
}
}
Output:
[Alice, Anna]
2. Transforming Data using map()
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List names = Arrays.asList("alice", "bob", "charlie");
// Converting all names to uppercase
List upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseNames);
}
}
Output:
[ALICE, BOB, CHARLIE]
3. Sorting Elements using sorted()
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class SortedExample {
public static void main(String[] args) {
List numbers = Arrays.asList(5, 2, 8, 1, 9);
List sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers);
}
}
Output:
[1, 2, 5, 8, 9]
4. Finding Minimum and Maximum using min()
& max()
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class MinMaxExample {
public static void main(String[] args) {
List numbers = Arrays.asList(3, 7, 2, 9, 4);
Optional min = numbers.stream().min(Integer::compareTo);
Optional max = numbers.stream().max(Integer::compareTo);
System.out.println("Min: " + min.get());
System.out.println("Max: " + max.get());
}
}
Output:
Min: 2
Max: 9
5. Grouping Elements using groupingBy()
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GroupByExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "apricot", "blueberry");
// Group words by first letter
Map<Character, List<String>> groupedWords = words.stream()
.collect(Collectors.groupingBy(word -> word.charAt(0)));
System.out.println(groupedWords);
}
}
Output:
{a=[apple, apricot], b=[banana, blueberry]}
Parallel Streams for Faster Execution
import java.util.stream.IntStream;
public class ParallelStreamExample {
public static void main(String[] args) {
System.out.println("Sequential Stream:");
IntStream.range(1, 6).forEach(System.out::println);
System.out.println("Parallel Stream:");
IntStream.range(1, 6).parallel().forEach(System.out::println);
}
}
Parallel streams divide work among multiple threads, improving performance for large datasets.
Conclusion
- Stream API enables functional programming in Java.
- Provides concise and readable code for collection processing.
- Intermediate & Terminal operations allow filtering, mapping, sorting, reducing, and more.
- Parallel streams improve performance on large datasets.