Java Streams Basics with Examples

In this article, we will learn Java Stream API or in other words Java Streams with examples. Java Streams comes with Java 8 and with stream operations, we can simplify many things such as filtering, sorting, transformations, etc. Let’s start to learn Java Streams with examples!

Java Streams with Examples

Let’s start to learn stream operations on a list. Assume that we have a list of integers from 1 to 10 and we will do a filter of the odd numbers in this list, then limit the execution of the elements after the 4th element, then transform the filtered elements by multiplying 2, and then finally print them. So, let’s list all operations that we will do on this integer list:

  • Filter the odd numbers
  • Limit the execution after 4th filtered element.
  • Transform the element by multiplying by 2.
  • Print the results.

We can do all of these operations in one stream chain or stream pipeline! This is the power of Java Stream API! :)

In the below example, the numbers list contains integers (it is a collection), and we are using numbers.stream() to convert the collection to a stream to do the stream operations. By using filter() method we filter odd numbers, then with the limit() method, we limit the stream processing, and map() method is the transformation method for streams. We use the map method to transform the filtered number to number*2. Finally, by using forEach() terminal stream operation to print the numbers which can come to the end of the stream chain.

public class StreamBasics {
    List<Integer> numbers = new ArrayList<>();

    @BeforeEach
    void setup() {
        //Another method: Collections.addAll(numbers, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);
        numbers.add(5);
        numbers.add(6);
        numbers.add(7);
        numbers.add(8);
        numbers.add(9);
        numbers.add(10);
    }

    @Test
    public void streamExample() {
        numbers.stream()
            .filter(number -> number % 2 == 1) //Filter the odd numbers.
            .limit(4) //After 4th element stop the stream. Process first 4 element.
            .map(number -> number * 2) //Transform the number to number*2
            .forEach(System.out::println); //Print the results.
    }
}

Output

java streams

Let’s do more examples, this time we will add some debugging lines in our stream code to see the execution process. 

@Test
public void streamOperationsTest() {
    System.out.println("Stream operations are starting!");
    
    numbers.stream()
        .filter(number -> {
            System.out.println("Filter operation for number: " + number);
            return number % 2 == 1;
        }) //Filter the odd numbers.
        .limit(4) //After 4th element stop the stream. Process first 4 element.
        .map(number -> {
            System.out.println("Map operation for number: " + number);
            return number * 2;
        }) //Transform the number to number*2
    .forEach(number -> System.out.println("Result of stream: " + number)); //Print the results.
    
    System.out.println("Stream operations finished!");
}

Output

As you can see below, each stream element is processed one by one. The odd numbers up to 4 which are 1, 3, 5, and 7 are processed and for them, you can see the filter, map, and result print lines. For example, the filter operation for number 2 immediately finished at filter operation because it is not an odd number and number 2 could not be processed until the end of the stream. And because of limit(4), after the 4th odd number which is 7, the stream operations finished.

Java stream api

Streams need terminal operations like forEach, collect, count, max, min, findAny, anyMatch, noneMatch, etc. If you do not finish stream one of the terminal operation, you will not get the results. You can test this by commenting .forEach() line in the below example. If we comment  .forEach(number -> System.out.println("Result of stream: " + number)); //Print the results. we cannot get anything as a result because streams need a terminal operation.

@Test
public void streamOperationsTest() {
    System.out.println("Stream operations are starting!");
    
    numbers.stream()
        .filter(number -> {
            System.out.println("Filter operation for number: " + number);
            return number % 2 == 1;
        }) //Filter the odd numbers.
        .limit(4) //After 4th element stop the stream. Process first 4 element.
        .map(number -> {
            System.out.println("Map operation for number: " + number);
            return number * 2;
        }); //Transform the number to number*2
    //.forEach(number -> System.out.println("Result of stream: " + number)); //Print the results.
    //Note: Streams need terminal operations! forEach, collect, count, max, min, findAny, anyMatch, noneMatch, etc.
    
    System.out.println("Stream operations finished!");
}

Output

java stream without terminal operation

We cannot also reuse the streams. Once we use a terminal operation for a stream that stream’s processing is finished and we cannot use one more terminal operation for the same stream. Let’s see this in an example.

@Test
public void streamReUsabilityTest() {
    Stream<Integer> numbersStream = numbers.stream()
        .filter(number -> number % 2 == 1) //Filter the odd numbers.
        .map(number -> number * 2);

    numbersStream.forEach(System.out::println);
    numbersStream.forEach(System.out::println); //Here we cannot reuse the stream again!!!
}

Output

As you seen below, the first terminal operation is worked fine and when we apply the second terminal operation on the same stream, we got an IllegalStateException: stream has already been operated upon or closed.”

stream reusability

In this article, I do not cover more topics such as comparator, intermediate operations, terminal operations, optional, stream sources, stream reduce, and more. We will cover these in the other articles. 

GitHub Project

https://github.com/swtestacademy/java-functional/blob/main/src/test/java/functional/stream/StreamBasics.java

Thanks for reading.
Onur Baskirt

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.