DEV Community

Priyank Bhardwaj
Priyank Bhardwaj

Posted on

Introduction to Streams

๐Ÿš€ What Are Streams?

A Stream in Java is a sequence of data elements supporting sequential and parallel aggregate operations.

Simply put, Streams let you process data declaratively โ€” what to do, not how to do it.

They bring a functional programming flavor to Java.

You can think of a Stream as a pipeline of operations that transform data step by step.

Key points:

  • Streams donโ€™t store data โ€” they operate on existing data sources (like Collections, Arrays, or I/O channels).
  • They are lazy โ€” computation happens only when a terminal operation is invoked.
  • They are functional โ€” once used, a Stream cannot be reused.

๐Ÿ†š Collections vs Streams

Feature Collections Streams
Nature Data structure (stores elements) Data pipeline (processes elements)
Iteration External (you control iteration with loops) Internal (handled by Stream API)
Reusability Can be reused Consumed once
Evaluation Eager (stores all elements) Lazy (computes on demand)
Example List<Integer> list = Arrays.asList(1,2,3); list.stream().filter(n -> n>1)

๐Ÿ‘‰ In short: Collections are about data storage, Streams are about data processing.


Lambda Notation Refresher

Before Streams, weโ€™d typically write something like:

for (String name : names) {
    if (name.startsWith("A")) {
        System.out.println(name);
    }
}
Enter fullscreen mode Exit fullscreen mode

With Lambdas and Streams, it becomes:

names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);
Enter fullscreen mode Exit fullscreen mode

See the difference?
No loops, no boilerplate โ€” just intent.

A lambda expression is essentially a short block of code that takes input and returns output, often used as an argument to Stream methods like filter(), map(), and forEach().


The Stream Pipeline

Every Stream operation flows through three main stages:

1. Source

Where data originates โ€” e.g., a collection, array, or file.

Stream<String> stream = List.of("Java", "Python", "C++").stream();
Enter fullscreen mode Exit fullscreen mode

2. Intermediate Operations

Transform or filter data.
These are lazy โ€” they donโ€™t do anything until a terminal operation is called.

Examples:
filter(), map(), sorted(), distinct(), limit()

stream.filter(lang -> lang.startsWith("J"))
      .map(String::toUpperCase);
Enter fullscreen mode Exit fullscreen mode

3. Terminal Operations

Trigger the actual processing and produce a result.

Examples:
forEach(), collect(), count(), findFirst()

long count = List.of("Java", "Python", "C++")
                 .stream()
                 .filter(lang -> lang.startsWith("J"))
                 .count();
System.out.println(count); // Output: 1
Enter fullscreen mode Exit fullscreen mode

Think of it as:

Source โ†’ Intermediate Operations โ†’ Terminal Operation
Enter fullscreen mode Exit fullscreen mode

Simple Examples to Get Started

Example 1: Filtering and Printing

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);

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

// Output: 2 4 6
Enter fullscreen mode Exit fullscreen mode

Example 2: Mapping to Another Type

List<String> words = List.of("java", "stream", "api");

List<String> upper = words.stream()
                          .map(String::toUpperCase)
                          .toList();

System.out.println(upper); // [JAVA, STREAM, API]
Enter fullscreen mode Exit fullscreen mode

Example 3: Combining Operations

List<String> names = List.of("Ayush", "Raj", "Anita", "Vikram");

long count = names.stream()
                  .filter(name -> name.startsWith("A"))
                  .map(String::toUpperCase)
                  .count();

System.out.println(count); // 2
Enter fullscreen mode Exit fullscreen mode

Coming Up Next

In upcoming parts, weโ€™ll deep dive into Intermediate Operations like:

filter(), map(), flatMap(), distinct(), sorted(), and more with real-world use cases and performance insights.

But before we go deeper into Stream operations, we need to understand the functional interfaces that make all this possible.

We'll explore:

  • Predicate, Function, Consumer, Supplier, and BiFunction
  • How they power Stream methods
  • When and how to create your own functional interfaces

Stay tuned and follow to get notified when the next part drops!

๐Ÿ”— Next Up (Part 2): Functional Interfaces You Must Know

Top comments (0)