Hello everyone,

Let's continue Stream API with intermediate operations.Stream API supported for two major operations. They are,

1) Intermediate Operation.

2) Terminal Operation.

In this tutorial I will go to the depth in the Intermediate Operations. Before continuing this tutorial I recommend you to read my Stream API and Intermediate Operation.

Intermediate Operation.

Operations that give back values like numbers or objects are called terminal operations. Unlike other operations, we can't link them in a chain. Terminal operations provide the final result, and after they're done, the stream is no longer usable. So, we can't use that stream again once a terminal operation is finished.

Ex :- toArray(), reduce(), collect(), min(),max(),count(), anyMatch(), allMatch(),findFirst(), foreach(), findAny()

How to use Terminal Operation In stream.

count() Operation

Syntax

long count()

We can use this method to count the number of elements in a finite stream.

Example 1)

For example, let's call the count() method to find the number of students in the stream below.

public class Main {
   public static void main(String[] args) {
       Stream<String> namesList = Stream.of("Mike","Anne","Sara","Robot");
       System.out.println("Count of Names :"+ namesList.count());
   }
}

Output

Count of Name :4

max() and min() Operations

The distinct() function returns the unique values from the original stream. In other words, it removes the duplicates. This distinct method uses the hashcode() and equals() method to determine the distinctness.

Method Signature

Optional<T> min(<? super T> comparator)
Optional<T> max(<? super T> comparator)

min() and max() functions allow us to find the min and max values in the finite stream. This also allows us to pass the custom comparator to find the largest and smallest value in the finite stream according to the sort order.

Example 1)
public class Main {
   public static void main(String[] args) {
   
       List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 6, 3);

       // Finding minimum number
       Optional<Integer> minNumber = numbers.stream()
               .min(Integer::compareTo);

       minNumber.ifPresent(min -> System.out.println("Minimum Number: " + min));

       // Finding maximum number
       Optional<Integer> maxNumber = numbers.stream()
               .max(Integer::compareTo);

       maxNumber.ifPresent(max -> System.out.println("Maximum Number: " + max));
   }
}

Remember that this function returns an Optional rather than the value. To print the minimum or maximum value, use the optional method along with a method reference. This way, you’ll display the value only if it exists.

findAny() and findFirst()

Method Signature

Optional<T> findFirst()
Optional<T> findAny()

findAny() method allows us to retrieve any element from a stream. This is useful when we’re looking for an element without paying attention to the encounter order. This method returns an Optional instance, which is empty if the stream is empty. In a non-parallel operation, it is likely that findAny() will return the first element in the stream, but this is not guaranteed. In a parallel operation, the result is not reliably determined due to performance optimizations.

findFirst() method used to find the first element in a stream. If there is no encounter order, it returns any element from the stream.The return type is also an Optional instance

Example 1)
public class Main {
   public static void main(String[] args) {
       List<String> fruits = Arrays.asList("Apple", "Banana", "Orange", "Mango", "Grapes");

       // Using findAny() to retrieve any element from the stream
       Optional<String> anyFruit = fruits.stream()
               .findAny();

       // Printing the result if any element is present
       anyFruit.ifPresent(fruit -> System.out.println("Any Fruit: " + fruit));
   }
}

Output

Any Fruit: Apple

Example 2)
public class Main {
   public static void main(String[] args) {
       // Create a list of names
       List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eva");
       
       // Use stream to find any name that starts with 'C'
       Optional<String> anyNameStartingWithC = names.stream()
               .filter(name -> name.startsWith("C"))
               .findAny();

       // Print the result
       if (anyNameStartingWithC.isPresent()) {
           System.out.println("Name starting with 'C' found: " + anyNameStartingWithC.get());
       } else {
           System.out.println("No name starting with 'C' found.");
       }
   }
}

Output

Name starting with 'C' found: Charlie

anyMatch( ), allMatch( ), and noneMatch( ) operations

This function allows you to sort elements in a collection (such as a list) based on a specified ordering.

Method Signature

boolean anyMatch(Predicate <? super T> predicate)
boolean allMatch(Predicate <? super T> predicate)
boolean noneMatch(Predicate <? super T> predicate)
anyMatch()

This method checks at least one element in the stream satisfies the given predicate. This method stops evaluating once it finds a matching element. As an example, If the fourth element satisfies the predicate, the rest of the stream elements won’t be processed. Conversely, if none of the elements match the predicate, the entire stream will be processed.

allMatch()

This method returns true, if all elements in the stream satisfy the provided predicate.If the first element doesn't meet the condition, the evaluation stops.

nonMatch()

The noneMatch() method checks if none of the elements in the stream satisfy the given predicate.It only evaluates the predicate on elements until it finds one that satisfies the predicate, then it returns false. It processes the entire stream only when none of the elements match the predicate.

Example
public class Main {
    public static void main(String[] args) {
           // Create a list of numbers
           List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
           // Check if any element is greater than 5
           boolean anyGreaterThanFive = numbers.stream()
                   .anyMatch(num -> num > 5);
       
           System.out.println("Any element greater than 5? " + anyGreaterThanFive);
        
           // Check if all elements are less than 20
           boolean allLessThanTwenty = numbers.stream()
                   .allMatch(num -> num > 20);
        
           System.out.println("All elements greater than 20? " + allLessThanTwenty);
       
           // Check if none of the elements are negative
           boolean noneNegative = numbers.stream()
                   .noneMatch(num -> num < 0);
        
           System.out.println("None of the elements are negative? " + noneNegative);
    }
}

Output :

Any element greater than 5? true All elements less than 20? false None of the elements are negative? true

forEach() Operation

Method Signature

void forEach(Consumer <? super T> action)

When iterating elements of a stream, it's important to note that forEach() is the only terminal operation with a return type of void. Additionally, it's worth mentioning that forEach() can be called directly on a Collection or on a Stream. It's also important to understand that the Stream API in Java 8 cannot use a traditional for loop to run because it doesn't implement the Iterable interface.

Example
public class Main {
   public static void main(String[] args) {
       Stream<String> fruits = Stream.of("Apple", "Banana", "Orange", "Mango", "Grapes");

       // Use forEach to print each element in uppercase
       fruits.forEach(fruit -> System.out.println(fruit.toUpperCase()));
   }
}

Output

APPLE BANANA ORANGE MANGO GRAPES

collect() Operation

Method Signature

There are two variants of the collect() method.

R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
R collect(Collector<? super T, A, R> collector)

Let’s break down the parameters:

  • supplier: A function that creates a new mutable result container. For parallel execution, this function may be called multiple times, and it must return a fresh value each time.

  • accumulator: A stateless function that folds an element into the result container.

  • combiner: A stateless function that merges two partial result containers. It must be compatible with the accumulator function.

This method performs a mutable reduction operation on the elements of the stream.What does “mutable reduction” mean? While processing the stream elements, it collects input elements into a mutable result container, such as a Collection or a StringBuilder.

Example
public class Main {
    public static void main(String[] args) {
           // Create a stream of numbers
           Stream<Integer> numberStream = Stream.of(11, 2, 33, 44, 5);
        
           // Use collect to transform the stream into a List
           List<Integer> numberList = numberStream.collect(Collectors.toList());
        
           // Print the result
           System.out.println("List of numbers: " + numberList);
    }
}

Reduce() Operation

Now you have a better understand about the intermediate operation in Stream API, how to use it, and the how to use that function using real world examples. In the next Tutorial, I will talk about Terminal Operations in Stream API (Terminal Operation).