Java Streams in an easy way

Rohit Khatana
2 min readMay 25, 2018

--

Since Java8 has been launched, I always wanted to write about the Java streams. Because using streams and lambdas, anyone can write declarative style of programming i.e. more concise, clean, expressive and easy to understand the code.

If you have written Scala, Python or Ruby then you know the power of map, filter, reduce etc. Using streams API we can use this type of power in Java also.

So, What are Steams?

The Stream is a pipeline of operations which can be evaluated and mutate the data.

For Example, let’s say we want to double the even number given in a list. This can be done easily this way:

list.streams()
.filter(i -> i % 2 == 0)
.map(i -> i *2)
.collect(Collectors.toList());

In above example, we focused on what to do instead of how to do it. This is what declarative programming is. Declarative programming focused on what instead of how.

Java Streams operations have been divided into intermediate and terminal operations.

All intermediate operations are lazily evaluated. And will not get evaluated if not followed by a terminal operator. All intermediate operations return a stream itself.

So in the above example, filter and map are intermediate operations which will be lazily evaluated and the collect is the terminal operation which will command the streams to evaluate the streams. And only numbers which are divisible by 2, will be passed to the map and then to the collect, which will convert it into a list.

Intermediate Operations: map, filter, peek, flatMap, limit, sort, distinct etc

Terminal Operations: findAny, findFirst, anyMatch, noneMatch, allMatch, collect, toArray, reduce, min, max, forEach, collect, count etc

Mostly every intermediate operation expects a Predicate function which can perform the action.

Predicate<Integer> doubleTheNumber = number -> number * 2;
list.streams()
.filter(i -> i % 2 == 0)
.map(doubleTheNumber)
.collect(Collectors.toList());

So We can write the lambda function like in Python using Predicate which is more readable and repeatable. Also, we can wrap the reusable functionality in a predicate, and by using the Function we can return a predicate which is more customizable.

Function<Integer, Predicate<Integer>> multiplyTheNumber = times -> number -> number * times;
list.streams()
.filter(i -> i % 2 == 0)
.map(multiplyTheNumber.apply(2)) //doubling the number
.collect(Collectors.toList());

By combining the Predicates and Streams API we can write more readable and simple code to understand.

Conclusion:

I hope I explained everything clearly enough for you to understand. If you have any questions, feel free to ask. You can find me on twitter.

Make sure you click on green heart below and follow me for more stories about technology :)

--

--