๐ 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);
    }
}
With Lambdas and Streams, it becomes:
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);
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();
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);
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
Think of it as:
Source โ Intermediate Operations โ Terminal Operation
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
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]
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
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, andBiFunction
- 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)