What are Intermediate and Terminal operations in Java8

Intermediate and terminal operations are concepts in the context of Java 8 streams, which are used to perform operations on collections of data in a functional way.

Intermediate operations are operations that are applied to a stream before the terminal operation. They take a stream as input, perform some processing, and produce a new stream as output. Examples of intermediate operations include filter(), map(), flatMap(), distinct(), sorted(), peek(), and limit(). Intermediate operations are lazy, which means they do not actually execute until a terminal operation is called.

Terminal operations , on the other hand, are operations that are applied to the stream after all intermediate operations have been applied. They produce a result or a side effect, and no further stream operations can be performed after a terminal operation has been called. Examples of terminal operations include forEach(), toArray(), reduce(), collect(), min(), max(), count(), and anyMatch(). Terminal operations trigger the execution of all the intermediate operations that were applied to the stream.

It's important to note that intermediate operations are not evaluated until a terminal operation is called, so multiple intermediate operations can be chained together without actually executing any processing until a terminal operation is called. This allows for efficient processing of large data sets.

Intermediate operations:

filter(): filters the elements of a stream based on a specified condition. This operation is used to filter elements based on a given predicate. It takes a Predicate<T> as an argument and returns a new stream that contains only the elements that satisfy the given predicate.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); Stream<String> filteredStream = names.stream().filter(name -> name.startsWith("C"));

map(): transforms the elements of a stream based on a specified function.This operation is used to transform elements of a stream using a given function. It takes a Function<T, R> as an argument and returns a new stream of type R that contains the results of applying the function to each element of the original stream.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4); Stream<Integer> squaredStream = numbers.stream().map(number -> number * number);

flatMap(): This operation is used to transform elements of a stream using a function that returns a stream. It takes a Function<T, Stream<R>> as an argument and returns a new stream that contains the flattened results of applying the function to each element of the original stream.

List<List<String>> nestedNames = Arrays.asList( Arrays.asList("Alice", "Bob"), Arrays.asList("Charlie", "David", "Emma") ); Stream<String> flatStream = nestedNames.stream().flatMap(names -> names.stream());

In this example, the flatMap() operation is used to create a new stream that contains all the names in the nested lists.

distinct(): This operation is used to remove duplicates from a stream. It returns a new stream that contains only the distinct elements of the original stream

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Bob"); Stream<String> distinctStream = names.stream().distinct();

In this example, the distinct() operation is used to create a new stream that contains only the distinct names from the original list.

sorted(): This operation is used to sort the elements of a stream. It takes a Comparator<T> as an argument and returns a new stream that contains the elements of the original stream in sorted order.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); Stream<String> sortedStream = names.stream().sorted();

limit(): This operation is used to limit the number of elements in a stream. It takes a long value as an argument and returns a new stream that contains the first n elements of the original stream.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> limitedStream = numbers.stream().limit(3);

In this example, the limit() operation is used to create a new stream that contains only the first three numbers from the original list.

skip(): This operation is used to skip the first n elements of a stream. It takes a long value as an argument and returns a new stream that contains all the elements of the original stream except the first n elements.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> skippedStream = numbers.stream().skip(2);

In this example, the skip() operation is used to create a new stream that contains all the numbers from the original list except the first two..

Terminal operations:

The most common type of terminal operation in Java 8 streams is forEach().

It is used to apply a given function to each element of a stream. It is used to collect the processed Stream data. .

forEach(): performs an action on each element of a stream.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); names.stream().forEach(name -> System.out.println(name));

Other commonly used terminal operations in Java 8 streams include:

collect(): collects the elements of a stream into a collection.

List name.startsWith("C")).sorted().collect(Collectors.toList());

reduce(): applies a binary operator to the elements of a stream to reduce them to a single value.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4); Optional<Integer> sum = numbers.stream().reduce((x, y) -> x + y);

  • count() - This operation is used to count the number of elements in a stream.
  • anyMatch() - This operation is used to check if any element of a stream matches a given predicate.
  • allMatch() - This operation is used to check if all elements of a stream match a given predicate.
  • noneMatch() - This operation is used to check if none of the elements of a stream match a given predicate
  • findAny() - This operation is used to find any element of a stream.
  • findFirst() - This operation is used to find the first element of a stream

These are just a few examples of intermediate and terminal operations in Java 8 streams. There are many more operations available that can be combined to perform complex data processing tasks.