Java Stream Basics

Jeff Li
3 min readJun 27, 2021

It has been years since Java 8 was released. No doubt it is a milestone for Java. One of the most attractive features is Lambda Expressions being introduced, which means Java brings developers more possibilities by embracing the functional programming paradigm. So today we plan to have a look at a functional style component supported: Stream.

What is a Stream?

A Stream is a sequence of elements.

The definition is concise but too abstract. It could be easier to compare with Collection cos they share some superficial similarities at first glance.

Speaking at a high level, they are designed for different goals. Collection is a data structure that provides the capability to manage and access elements in a more efficient way. So you can manipulate elements by using the Collection API, like get, set, and etc. However, Stream focuses on performing computational operations on these elements. It does not intend to provide methods for manipulating elements directly.

Simply put, Collection is a container while Stream is designed for computing on a source(the source can be a collection or something else).

Concepts and terms

Stream introduces a heavy conceptual weight. It can be a little bit harder to understand it, especially for developers who get used to imperative programming. So it could be better to start with some sub-concepts living in Stream.

Aggregate Operation: the operation processes elements from a Stream, like filter, map, and etc. It can also be called Stream Operation cos it is performed on a Stream. Generally, there are two different Aggregate Operations:

  • intermediate operation: the operation produces a Stream. So you can continue to do more operations on the result produced by that operation. For example, filter is an intermediate operation and it produces a Stream. Then, you can filter{..}.anotherOperation{..} .
  • terminal operation: the operation produces a Non-Stream result. So you can not continue to do more operations after a terminal operation. For example, forEach , max , average , and etc are terminal operations. One of them is called reduce which is necessary to put a mark on. reduce means reduce the contents of a Stream, possibly holding multiple elements, to one value.

Pipeline: a sequence of aggregate operations, Like: intArray.stream().filter{..}.map{..}.forEach{..}. By definition, a stream pipeline consists of:

  • a source: it is where the elements come from. A source can be a collection, an array, an HTTP request and etc. Usually, a source class provides API for creating a Stream, like Collection.stream() .
  • zero or more intermediate operations: intermediate operations are not a must for a pipeline.
  • a terminal operation: by contrast, there must be exactly one terminal operation in a pipeline.

It is a little bit verbose for explaining these terms. Here is an analogy diagram:

As you can see, a Stream carries elements from a source through a pipeline.

Summary

We might stop here for a basic introduction even though there is still a lot of interesting stuff in Java Stream, like parallelism, ordering, side effects, and etc. We can discuss them one by one later on.

Everything has its pros and cons. Java Stream is not an exception. While bringing us convenience, it also imposes some challenges, like handling exceptions, debugging, and so on. However, declarative programming is more and more appealing nowadays. Actually, it is fairly mature in SQL. For now, our tools, machines are not that smart to make declarative programming being used widely. The unintelligence from tools reflects limitations on us. I am not qualified to announce it is the future. But I wish I can see more and more these things happening around me.

Is Java Stream good or evil? Only time knows.

--

--