{{announcement.body}}
{{announcement.title}}

Hero Without a Cape: Stream API

DZone 's Guide to

Hero Without a Cape: Stream API

Let's learn more about this Java hero!

· Java Zone ·
Free Resource

Let's learn more about this Java hero!

In this post, we will be talking about the Stream API added to Java 8 and how it has changed the way we do programs in Java. It added neatness as well as made the code more readable. It helps conduct functional programming in Java. So without any further ado, let's learn more about this Java gem!

You may also like: Your Guide to Java Streams [Tutorials & Articles]

What Is the Stream API?

The Stream API, included in Java 8, is utilized for processing collections of Objects. It is the flow of Objets on which various methods get applied in the pipeline to have the result. Simply put, it flows through the given Collection<Object> and applies the different methods on the Object to aggregate them into the desired result without affecting the original Object.

What Stream Provides

Stream doesn’t store anything; instead, it operates on the given Collection, Array, or I/O (yes, you can use it on I/O). Without changing the original data, it applies the methods to the data. Lazy evaluation helps to add multiple intermediate operations without running the full stream. Evaluated only after we add terminal operation. it adds Neatness and make codes cleaner. Produce a more comprehensive code. And many more.

Things to Know Before You Go

Stream allows you to practice functional programming in Java. This means that you should be comfortable with functional interfaces and lambda expressions. If you understand these concepts well, no one can stop you from understanding the Stream API. You can find code examples on GitHub or view the full project here.

How to Stream

Streams can be implemented in multiple ways. A few magic tricks are:

  • Collection’s stream() and parallelStream() methods
  • Via Arrays.stream(Object[])
  • Stream’s static factory methods such as Stream.of(Object[]), IntStream.range(int, int), or Stream.iterate(Object, UnaryOperator)
  • Streaming the line of a file by BufferedReader.lines()
  • Streams of random numbers can be obtained from Random.ints().

The first and second bullets are what we use most often. For our discussion, we will be sticking to the Collection#steam() method. For example:

Java




x
10


 
1
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5,
2
       6, 7, 8, 9, 10);
3
//start the stream
4
list.stream()
5
        // multiplying each number by 2
6
        .map(n -> n * 2)
7
        //taking forward only even numbers
8
        .filter(n -> n % 2 == 0)
9
        //printing each even number
10
        .forEach(n -> System.out.println(n));



Stream Pipeline Operations

We can perform two operations on a Stream pipeline:

  • Intermediate Operation
  • Terminal Operation

Intermediate operation transforms or filters the object in the preceding pipeline. These methods are nothing but the Functional Interfaces we mentioned previously. You should get familiar with them to easily understand this operation without any trouble.

Stream won’t execute intermediate operations unless the terminal operation is called. It is an indication for the Stream to start the pipeline. Once we call the terminal operation, the pipeline ends and it returns the desired result. After the terminal operation, we can't use intermediate operations since the pipeline has ended. It can only be called once at the end of the pipeline. However, you can again start the stream if the terminal operation returns a collection — which I highly oppose doing. You can pretty much do all the things inside a single stream pipeline only.

Intermediate Operations

In the Stream pipeline, we can have multiple intermediate operations that transform or filter the data which will be flowing towards the next pipe without affecting the actual data on the Collection. These operations return the Stream of the desired result instead of the actual result through, which we can add more intermediate operation without breaking the Stream pipeline. We will be talking about the map and filter in this post and others in another article. Methods map and filter are the ones you will be needing most of the time.

These methods are applied one by one to the elements in the pipeline and not all at once.

map(Function)

Think of map(Function) as a transforming function that transforms the given object to another. It accepts a Function as a parameter, which takes the element in the stream as an input, performs some operation we tell it to, and returns the Stream of the resulting object. If we look at our previous example, it is multiplying each number by 2. We can have another map that can add 2 to the number and then another one to convert it to another object, and then another one, and so on — you get the idea.

Java




xxxxxxxxxx
1
11


1
list.stream()
2
        // 1. multiplying each number by 2
3
        .map(n -> n * 2)
4
        // 2. Converting to string.
5
        .map(n -> "CodersTea.com-Post No : " + n)
6
        //3. Now you will be working on the
7
        //stream of String
8
        //4. we add another string to the previous
9
        .map(string -> string + ". Another String")
10
        //print the end result
11
        .forEach(System.out::println);



In the above example, look at how we transformed a list of Integer to the String. Let's break it down:

  1. Multiplying each number by 2. So, 1 becomes 2, 2 becomes 4, and so on.
  2. We have attached the number to a string resulting in a String Object.
  3. The previous map made the Stream<Integer> to Stream<String> due to the return type of string.
  4. We again attached another string to perform some operation.
  5. Finally printing the results, which looks like:

Output:

Java




x


1
CodersTea.com-Post No : 2. Another String
2
CodersTea.com-Post No : 4. Another String
3
CodersTea.com-Post No : 6. Another String
4
CodersTea.com-Post No : 8. Another String
5
CodersTea.com-Post No : 10. Another String
6
CodersTea.com-Post No : 12. Another String
7
CodersTea.com-Post No : 14. Another String
8
CodersTea.com-Post No : 16. Another String
9
CodersTea.com-Post No : 18. Another String
10
CodersTea.com-Post No : 20. Another String



filter(Predicate)

The filter, as the name suggests, filters out the elements before it reaches the next pipe. Simply put, if the element satisfies the condition, it allows it to pass through; otherwise, it won’t allow for the next operation. It takes a Predicate as a parameter.

Java




xxxxxxxxxx
1
16


 
1
long count = list.stream()
2
        // 1. Need only even numbers
3
        .filter(n -> n % 2 == 0)
4
        // 2. Checking if the Number is
5
        // greater than 5
6
        .filter(n -> n > 5)
7
        //3. multiplying the number by 5
8
        // just to show how we can use multiple
9
        // Intermediate operation together
10
        .map(n -> n * 5)
11
        // 4. Any number less than 50
12
        .filter(n -> n < 50)
13
        // Counting how much elements
14
        // survived teh pipeline
15
        .count();
16
System.out.println("Total " + count + " numbers survived the storm");



So many filters. Ok, let's break this down, shall we?

  1. Take only even numbers ahead.
  2. Forward only numbers greater than 5
  3. Multiply the number by 5. Again, just to show you can have multiple intermediate operations in a single Stream pipeline.
  4. Allow numbers that are less than 50.
  5. Finally, count (Terminal Operation) how many survived after this much filtering.

Output:

Java




xxxxxxxxxx
1


1
Total 2 numbers survived the storm



Terminal Operation

Until now, we have seen operations perform something in the pipeline and return the stream. In the end, we will be needing a final result. That’s where terminal operation comes in. It not only gives us the desired result, but it also starts the Stream. As I said, Stream is lazy; it won’t do anything, no matter how many intermediate operations we add to the pipeline, unless it sees its end, the terminal operation.

You cant use the same stream after it was closed by a terminal operation. Otherwise, it will throw IllegalStateException: stream has already been operated upon or closed. You have to start a new stream

The terminal operations we have used in the above examples are forEach and count. Let us examine them and a few others below.

forEach(Consumer)

forEach(Consumer) takes the input but returns no output, taking Consumer as a parameter. Use this if you want to do something without returning anything. We have used it for printing the numbers.

Java




xxxxxxxxxx
1


 
1
list.stream()
2
        // square of each number
3
        .map(n -> n * n )
4
        .forEach(n -> System.out.print(" "+ n));
5
        //output: 
6
        // 1 4 9 16 25 36 49 64 81 100



count()

As the name suggests, it is used for counting the elements reached to the end. Like in our filter example, counting who satisfied all the filter’s conditions.

Java




xxxxxxxxxx
1


 
1
list.stream()
2
        // even numbers less than 5s
3
        .filter(n -> n < 5 && n %2 == 0)
4
        .count(); // returns 2



collect()

I use this more often than any other terminal operation. It allows you to create the Collection from the elements of the stream after processing them. These Collection can be List, Set, or Map. The Collection includes elements that passed through the last pipe.

Java




xxxxxxxxxx
1
10


1
List<Integer> processedList = list.stream()
2
        // multiplying by 5
3
        .map(n -> n * 5)
4
        // numbers less than 30
5
        .filter(n -> n < 30)
6
        //add  +2
7
        .map(n -> n + 2)
8
        // give the list of processed numbers
9
        .collect(Collectors.toList());
10
System.out.println("processed List is \n " + processedList);



In the pipeline, we have multiplied the number by 5, then filtered numbers less than 30 and added 2. And after all the elements are done processing, we collect them into the list.

Java




xxxxxxxxxx
1


1
processed List is
2
 [7, 12, 17, 22, 27]



Conclusion

We have studied what the Stream API is, how to obtain it, and how to best use it. This post is kind of like an introductory post for the Stream API. There are so many diamonds in the mine, which we will be mining in our next post.

You can find the source code on GitHub or the full project here.

Further Reading 

Your Guide to Java Streams [Tutorials & Articles]

Overview of Java Stream API Extensions

What's in a Name: Java Naming Conventions

Becoming a Master of Java Streams Series: Part 1, 2, 3, 4, 5, & 6

Topics:
java ,stream ,stream api ,java8 ,functional programming ,loop

Published at DZone with permission of Imran Shaikh . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}