Code Pumpkin

Stream API | Java8

May 12, 2017
Posted by Dipen Adroja

In this article, we are going to walk through Stream API which is introduced as a part of Java8. Using Stream API, we can make our collection manipulation more declarative and thread safe in case of multi-threaded environment with very ease.

The advantage of using Stream API to manipulate collections is we only ask the Java framework what we want to do without taking control of iteration. Before going into more details, I would recommend you to go through Lambda expressions.

Now lets move to Stream API Basics. By the end of the article, we will be able to answer following questions:

  1. What is streams?
  2. How it can be created?
  3. Stream operations?
  4. Difference between stream and collections?

1. What is streams? 

Let's start our quest with first question, java helps us to process our data in more declarative way as we can do in SQL queries.

Java stream doesn't store data. It operates on the source data structure (ex: collection and array) and produce pipelined data that we can use and perform specific operations.

In addition to that java stream APIs also help us to use parallelism as these APIs supports many parallel operations to process the data.

Before going into more details of stream API, We will first go into one basic example of collection manipulation without Stream and with Stream. Program will print all the even number present in the collection.


//Without stream API
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
				
for(int i:numbers){
	if(i%2 ==0)
	System.out.println(i);
}

//With stream API
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream().filter(e -> e%2 ==0)
				.forEach(e -> System.out.println(e));

In the above example we have seen one example using external iterator while other using the stream API and internal iterator.


2. How it can be created? 

There are many ways to create stream API. Below are some of different ways to build streams from collections.

1) Using Stream.of(arrayOfElements)


Stream<Integer> stream = Stream.of( new Integer[]{1,2,3,4,5,6,7,8,9} );
stream.forEach(p -> System.out.println(p));

2) Using Stream.of(val1, val2, val3….)


Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9);
stream.forEach(p -> System.out.println(p));

3) Using someList.stream()

Collection framework have introduced stream instance method using default method in interface. So you can get stream from any collection object via stream() or parallelStream() method.


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

for(int i = 1; i< 10; i++){
 list.add(i);
}

Stream<Integer> stream = list.stream();
stream.forEach(p -> System.out.println(p));

4) Using String chars or String tokens


IntStream stream = "12345_abcdefg".chars();

stream.forEach(p -> System.out.println(p));

5) Using Stream.generate() or Stream.iterate() functions


Stream<Date> stream = Stream.generate(() -> { return new Date();});

stream.forEach(p -> System.out.println(p));

Streams can be defined as a sequence of elements from a source that supports aggregate operations on them.
The source here refers to a Collection or Arrays who provides data to a Stream. Stream keeps the order of the data as it is in the source.

java.util.Stream represents a stream on which one or more operations can be performed. Stream operations are either intermediate or terminal. While terminal operations return a result of a certain type, intermediate operations return the stream itself so you can chain multiple method calls in a row. Stream operations can either be executed sequential or parallel.


3. Stream operations:

There are many operations supported by stream API. Stream operations mainly divided into two section and they are combined to form a pipeline:

  1. Intermediate Operations
  2. Terminal Operations

1) Intermediate Operations: 

Intermediate operations return the new stream itself so you can chain multiple method calls in a row.

They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

Some of the frequently used intermediate operations are: filter, map, sorted

A) filter()
Filter accepts a predicate to filter all elements of the stream. This operation is intermediate which enables us to call another stream operation (e.g. forEach) on the result.



numbers.stream().filter(e -> e%2 ==0)
				.forEach(e -> System.out.println(e));

output:

2
4
6
8
10

B) map()
The intermediate operation map converts each element into another object via the given function. The following example filters all the even number and then sum that number But you can also use map to transform each object into another type.



numbers.parallelStream().filter(e -> e%2 ==0)
					    .mapToInt(e -> e *2)
						.sum()

output:

60

C) sorted()
Sorted is an intermediate operation which returns a sorted view of the stream. The elements are sorted in natural order unless you pass a custom Comparator.



numbers.stream().sorted().forEach(e -> System.out.println(e));

Output:
 
1
2
3
4
5
6
7
8
9
10

Keep in mind that sorted does only create a sorted view of the stream without manipulating the ordering of the backed collection. The ordering of memberNames is untouched.

2) Terminal operations

Terminal operations such as Stream.forEach or IntStream.sum return a result of a certain type instead of again a Stream. They may traverse the stream to produce a result or a side-effect.

After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream.

In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator() are not eager. 

Some of the frequently used terminal operators are: forEach, collect, count, match, findFirst

A) forEach()

This method helps in iterating over all elements of a stream and perform some operation on each of them. The operation is passed as lambda expression parameter.



numbers.stream().forEach(e -> System.out.println(e));

B) collect()

collect() method used to receive elements from a stream and store them in a collection and mentioned in parameter function.



numbers.parallelStream().filter(e -> e%2 ==0)
			         	 .mapToInt(e -> e *2)
                         .collect(Collectors.toList());
System.out.print(numbers);

Outpout: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

C) match()

Various matching operations can be used to check whether a certain predicate matches the stream. All of those operations are terminal and return a boolean result.



System.out.print(numbers.stream().anyMatch(e -> e ==2));

Output: 
 true

D) count()

Count is a terminal operation returning the number of elements in the stream as a long.



List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,1,2,3,1,1,1);
 
System.out.print(numbers.stream().filter(e -> e ==1).count());

Output: 5

4. Difference between stream and collections?

A collection is an in-memory data structure to hold values and before we start using collection, all the values should have been populated. Whereas a java Stream is a data structure that is computed on-demand.

Java Stream doesn't store data, it operates on the source data structure (collection and array) and produce pipelined data that we can use and perform specific operations. Such as we can create a stream from the list and filter it based on a condition. It uses lambda expressions which makes our code more declarative, short and readable.


To make the stream processing faster by taking advantage of multiple-cores, we can simply use parallelStream() instead of stream(). This will takes care of all the threading issues which will make our code more robust.

That's all for this topic. If you guys have any suggestions or queries, feel free to drop a comment. We would be happy to add that in our post. You can also contribute your articles by creating contributor account here.

Happy Learning 🙂

If you like the content on CodePumpkin and if you wish to do something for the community and the planet Earth, you can donate to our campaign for planting more trees at CodePumpkin Cauvery Calling Campaign.

We may not get time to plant a tree, but we can definitely donate ₹42 per Tree.



About the Author


Coder, Blogger, Wanderer, Philosopher, Curious pumpkin



Tags: , ,


Comments and Queries

If you want someone to read your code, please put the code inside <pre><code> and </code></pre> tags. For example:
<pre><code class="java"> 
String foo = "bar";
</code></pre>
For more information on supported HTML tags in disqus comment, click here.
Total Posts : 124
follow us in feedly

Like Us On Facebook