In Java Stream API, operations are classified into two main types: intermediate operations and terminal operations. Intermediate operations are those that transform a stream into another stream, allowing you to perform various transformations, filtering, and mapping. Terminal operations, on the other hand, produce a result or a side-effect and trigger the processing of the stream. Here is a list of commonly used terminal and intermediate operations in the Java Stream API:
Intermediate Operations:
filter(Predicate<T> predicate)
- Returns a stream consisting of elements that match the given predicate.
map(Function<T, R> mapper)
- Returns a stream consisting of the results of applying the given function to the elements of the stream.
flatMap(Function<T, Stream<R>> mapper)
- Returns a stream consisting of the concatenated results of applying the given function to the elements of the stream.
distinct()
- Returns a stream consisting of distinct elements.
sorted()
- Returns a stream sorted according to their natural order.
peek(Consumer<T> action)
- Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
limit(long maxSize)
- Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
skip(long n)
- Returns a stream consisting of the remaining elements of this stream after discarding the first n elements.
Terminal Operations:
forEach(Consumer<T> action)
- Performs an action for each element of the stream.
toArray()
- Returns an array containing the elements of this stream.
collect(Collector<T, A, R> collector)
- Performs a mutable reduction on the elements of this stream using a Collector.
count()
- Returns the count of elements in the stream as a long.
reduce(T identity, BinaryOperator<T> accumulator)
- Performs a reduction on the elements of this stream, using an associative accumulation function and returns an Optional.
min(Comparator<T> comparator)
- Returns the minimum element according to the provided Comparator.
max(Comparator<T> comparator)
- Returns the maximum element according to the provided Comparator.
anyMatch(Predicate<T> predicate)
- Returns true if any elements of the stream match the given predicate.
allMatch(Predicate<T> predicate)
- Returns true if all elements of the stream match the given predicate.
noneMatch(Predicate<T> predicate)
- Returns true if no elements of the stream match the given predicate.
findFirst()
- Returns an Optional describing the first element of the stream.
findAny()
- Returns an Optional describing any element of the stream.
These operations allow developers to build complex data processing pipelines using a combination of intermediate and terminal operations. Understanding these operations and their use cases is crucial for effective stream usage in Java.
Let's go through examples for both terminal and intermediate operations in Java Stream API:
Intermediate Operations:
filter(Predicate<T> predicate)
- Example: Filtering even numbers.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
map(Function<T, R> mapper)
- Example: Mapping names to their lengths.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
flatMap(Function<T, Stream<R>> mapper)
- Example: Flattening nested lists.
List<List<String>> nestedLists = Arrays.asList(
Arrays.asList("Java", "C++"),
Arrays.asList("Python", "JavaScript")
);
List<String> flattenedList = nestedLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
distinct()
- Example: Getting distinct elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 1, 2, 3, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
sorted()
- Example: Sorting strings.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
peek(Consumer<T> action)
- Example: Peeking at each element while processing.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> processedNames = names.stream()
.peek(System.out::println)
.collect(Collectors.toList());
limit(long maxSize)
- Example: Limiting the number of elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> limitedNumbers = numbers.stream()
.limit(5)
.collect(Collectors.toList());
skip(long n)
- Example: Skipping the first few elements.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> skippedNumbers = numbers.stream()
.skip(5)
.collect(Collectors.toList());
Terminal Operations:
forEach(Consumer<T> action)
- Example: Printing each element.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
toArray()
- Example: Converting to an array.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String[] namesArray = names.stream().toArray(String[]::new);
collect(Collector<T, A, R> collector)
- Example: Collecting to a List.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> collectedNames = names.stream().collect(Collectors.toList());
count()
- Example: Counting elements.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
long count = names.stream().count();
reduce(T identity, BinaryOperator<T> accumulator)
- Example: Calculating the sum.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
min(Comparator<T> comparator)
- Example: Finding the minimum element.
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2);
Optional<Integer> minNumber = numbers.stream().min(Integer::compareTo);
max(Comparator<T> comparator)
- Example: Finding the maximum element.
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2);
Optional<Integer> maxNumber = numbers.stream().max(Integer::compareTo);
anyMatch(Predicate<T> predicate)
- Example: Checking if any element matches a condition.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
boolean anyMatch = names.stream().anyMatch(name -> name.startsWith("B"));
allMatch(Predicate<T> predicate)
- Example: Checking if all elements match a condition.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
boolean allMatch = names.stream().allMatch(name -> name.length() > 2);
noneMatch(Predicate<T> predicate)
- Example: Checking if no element matches a condition.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
boolean noneMatch = names.stream().noneMatch(name -> name.endsWith("Z"));
findFirst()
- Example: Finding the first element.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> firstElement = names.stream().findFirst();
findAny()
- Example: Finding any element.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> anyElement = names.stream().findAny();
These examples illustrate the use of various intermediate and terminal operations in Java Stream API, providing a versatile and expressive way to process collections of data.