Core Java

Introduction to Java 8 Stream API


Java 8 introduced functional programming and the concept of streams and functional interfaces to back it. As the name suggests, Java 8 Stream can be thought of as a stream or source of data to operate on.

For this tutorial, we assume the reader to have an understanding of Java Optional and Functional Interface.

Why use Java Streams?

Java Stream helps us to reduce our lines of code significantly. For instance, suppose we have a List of marks:

List<Integer> listOfMarks = new ArrayList<>();

The solution to finding the average of all marks in listOfMarks for Java 7 or lower versions would look something like:

public double getAverageMarks(List<Integer> listOfMarks) {
    int totalMarks = 0;
    for(int marks : listOfMarks) 
        totalMarks += marks;
    return (double)totalMarks/listOfMarks.size();

With the help of Java 8 Streams, we can write replace the above code with a one-liner:

double avg = -> i).average().getAsDouble();

This simple example is enough for us to realize the power of Streams API.

Instantiating Streams:

Some of the most common ways to instantiate a stream involve:

1.), value2, …) package supports many operations over a Stream. An easy way to say open a stream over a given list of integers and print them would be:

Stream.of(1, 2, 3, 4).forEach(System.out::println);

where Stream is a class in package.


We can also use an array to instantiate a stream:

Stream.of(new Integer[] {1, 2, 3, 4}).forEach(System.out::println);
3.) Using stream() method over a Collection

Opening a stream say over a list of elements or any other Collection will make use of stream() method:

Stream<Integer> streamOfMarks  =;

The Stream.generate() method accepts a Supplier<T> which accepts an input source which acts as an infinite stream. The programmer is expected to specify the limit of the stream size:

Stream<String> streamGenerated = Stream.generate(() -> "programmerGirl").limit(50);

This method accepts the iteration logic to be used for instantiating a stream. Let’s look at an example:

Stream<Integer> iteratedStream = Stream.iterate(2, i -> i + 2).limit(50);

The above produces a stream over elements – 2, 4,…100 i.e. a total of 50 elements.

6.) Stream of Characters

chars() method in String class can help us open a stream of characters:

IntStream streamOfChars = "programmerGirl".chars();

Stream Intermediate Operations:

Intermediate operations in Stream are the ones that produce another Stream as an output. With this, we can chain multiple such methods.

There are innumerous operations supported by Streams. In this article, we’ll only cover the most commonly used operations –  map() and filter().

1.) filter():

As the name suggests, filter() method filters out the elements from our source stream based on the provided condition. The condition used to filter is provided in the form of a Predicate<T>: -> i >= 90)
  .forEach(System.out::println); //only prints 90 and 98
2.) map():

map() operation helps us transform the elements in a Stream from one type to another. For example:

Stream<Integer> intStream = Stream.of("1", "2", "3").map(Integer::parseInt);

There’s another operation called flatMap(). We’ll suggest you reading this article to understand the map and flatMap operation in detail.

Other Operations:

Some other common operations over Stream involves:

1.) count() method:

It returns the count() of elements present in our stream:

long count = Stream.of(1, 2, 3).count(); //outputs 3

Note that the return type of this method is long, not int.

2.) forEach() method:

It helps in iterating over the elements of our stream:

Stream.of(1, 2, 3).forEach(System.out::println);
3.)reduce() method:

It helps in performing some sort of reduction over a given stream. An easy example would be:

OptionalInt reducedValue = IntStream.rangeClosed(1, 5).reduce((i, j) -> i + j);
4.) anyMatch()/allMatch()/noneMatch():

All of these methods have a boolean output and are used to match a specific condition:

boolean matched = Stream.iterate(1, i -> i+1).limit(100).anyMatch(n -> n==50); //true

boolean noneMatched = Stream.iterate(1, i -> i+1).limit(100).noneMatch(n -> n==50); //false

boolean allMatched = Stream.iterate(1, i -> i+1).limit(100).allMatch(n -> n > 0); //true

Name of the methods are self-explanatory. Still, we’ll suggest you trying out a couple of examples.

5.) findFirst():

findFirst() returns the first element from the stream as an Optional. As soon as the first element is found, it stops any further processing:

int firstElement = Stream.iterate(1, i -> i+1).limit(100).findFirst().get();

To retrieve the element back from the Optional<Integer>, we have used get() method, since we already have a fixed stream. However, as we know it’s recommended to do a isPresent() check first or else it might throw NoSuchElementException for an empty stream.

Collecting Output Stream:

The stream can be collected into a Collection or an Array. Let’s look at the approaches:

1.) Using collect() method:

This method helps us to collect the output generated using a Stream. For example:

List<Integer> listOfEvenIntegers = Stream.iterate(1, i -> i+1).limit(100).filter(i -> i%2 == 0).collect(Collectors.toList());

On executing the above code, listOfEvenIntegers will store 2, 4, ,6, 8…100.

2.) Using toArray(ElementType[]::new):

To collect a Stream into an Array, we’ll use something like:

Integer[] arrayOfEvenIntegers = Stream.iterate(1, i -> i+1).limit(100).filter(i -> i%2 == 0).toArray(Integer[]::new);


In this tutorial, we got introduced to Java 8 Streams API. We looked at ways of instantiating a Stream followed by covering a few common operation over stream.

We also learned to recollect the output stream in a List or an array.

Be the First to comment.

Leave a Comment

Your email address will not be published. Required fields are marked *