Java Streams - Java Stream Operation








External Iteration

When operating with Java Collections we use external iteration.

In external iteration we use a for or for each loop or obtain an iterator for a collection and process elements of the collections in a sequence.

The following code computes the sum of squares of all odd integers in the list.

It uses for each loop to access every single element in the list then uses if statement to filter odd integers.

After that it calculates the square and finally stores the sum of square with sum variable.

import java.util.Arrays;
import java.util.List;
/*from w  w  w.  j a va 2 s .co  m*/
public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = 0;
    for (int n : numbers) {
      if (n % 2 == 1) {
        int square = n * n;
        sum = sum + square;
      }
    }
    System.out.println(sum);
  }
}

The code above generates the following result.





Internal Iteration

We can rewrite the code above using stream. It does exactly the same.

import java.util.Arrays;
import java.util.List;
/*  w  ww.ja v  a2s  .c om*/
public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.stream()
        .filter(n -> n % 2  == 1)
        .map(n  -> n * n)
        .reduce(0, Integer::sum);

    System.out.println(sum);
  }
}

In the code above we didn't use loop statement to iterate through the List. We did the loop internally by stream.

For the odd integer calculation we used lambda expression. We first did the filter then map then reduce.

The code above generates the following result.





Sequential

The external iteration typically means sequential code. The sequential code can be executed only by one thread.

Streams are designed to process elements in parallel.

The following code computes the sum of squares of odd integers in the list in parallel.

import java.util.Arrays;
import java.util.List;
/*  w ww  .j a v  a2s.  c  o  m*/
public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.parallelStream()
        .filter(n -> n % 2  == 1)
        .map(n  -> n * n)
        .reduce(0, Integer::sum);

    System.out.println(sum);
  }
}

What we did is just to replace stream() with parallelStream().

Parallel computation is easy when using the internal iteration provided by the stream.

The code above generates the following result.

Imperative vs. Functional

In imperative programming we control not only what to do but also how to do it.

For example, when using imperative programming to sum integers in a list. We have to decide how to iterate each element in the list. We can use for loop, for-each loop, or we can get an Iterator object from list and use while loop. Then we also have to do the sum.

In declarative programming we just need to tell what to do, the how part is handled by the system itself.

Collections support imperative programming whereas streams support declarative programming.

The Streams API supports the functional programming by using lambda expression.

What operations we want to perform on the stream elements are done typically by passing a lambda expressions.

Operations on a stream produce a result without modifying the data source.

Intermediate operations Terminal operations

A stream supports two types of operations:

  • Intermediate operations
  • Terminal operations

Intermediate operations are also called lazy operations.

Terminal operations are also called eager operations.

A lazy operation does not process the elements until an eager operation is called on the stream.

An intermediate operation on a stream produces another stream.

Streams link operations to create a stream pipeline.

In the following code filter() and map() are all lazy operations. While reduce() is eager operation.

import java.util.Arrays;
import java.util.List;
/*  ww w .j  a v  a 2 s  .  co m*/
public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.parallelStream()
        .filter(n -> n % 2  == 1)
        .map(n  -> n * n)
        .reduce(0, Integer::sum);

    System.out.println(sum);
  }
}

The code above generates the following result.