Stream API Basics

Back

Loading concept...

🌊 Stream API Basics: The Magic Water Slide of Data!

Imagine you have a big bucket of colorful marbles. You want to pick only the blue ones, make them shiny, and line them up nicely. Doing this one marble at a time is slow and boring. What if you had a magic water slide that could do all of this automatically?

That’s exactly what Java Streams are! A Stream is like a water slide for your data. You pour data in at the top, it flows through different stations (filters, transformers), and out comes exactly what you want at the bottom!


🎬 Stream API Introduction

What is a Stream?

A Stream is NOT a collection. It’s more like a conveyor belt at a toy factory.

// Your marbles (a List)
List<String> marbles = Arrays.asList(
    "red", "blue", "green", "blue"
);

// The magic water slide (Stream)
marbles.stream()
    .filter(m -> m.equals("blue"))
    .forEach(System.out::println);
// Output: blue, blue

Think of it this way:

  • πŸ“¦ Collection = A box holding your toys
  • 🌊 Stream = A slide that moves toys through checkpoints

Why Use Streams?

Old Way (Loops) New Way (Streams)
Write lots of code Write less code
Tell computer HOW to do it Tell computer WHAT you want
Harder to read Reads like English

🚰 Creating Streams

Before you can slide, you need to get ON the slide! Here are the ways to create streams:

1. From a Collection

List<Integer> numbers = Arrays.asList(
    1, 2, 3, 4, 5
);
Stream<Integer> numStream = numbers.stream();

2. From an Array

String[] fruits = {"apple", "banana", "cherry"};
Stream<String> fruitStream = Arrays.stream(fruits);

3. Using Stream.of()

Stream<String> colors = Stream.of(
    "red", "green", "blue"
);

4. Empty Stream

Stream<String> empty = Stream.empty();

5. Infinite Streams (with limit!)

// Numbers: 0, 1, 2, 3, 4...
Stream<Integer> counting = Stream.iterate(
    0, n -> n + 1
).limit(5);

// Random numbers
Stream<Double> randoms = Stream.generate(
    Math::random
).limit(3);
graph TD A["πŸ“¦ Collection"] --> B["🌊 .stream"] C["πŸ“‹ Array"] --> D["🌊 Arrays.stream"] E["✨ Values"] --> F["🌊 Stream.of"] B --> G["Ready to Flow!"] D --> G F --> G

πŸ”§ Intermediate Stream Operations

Intermediate operations are like checkpoints on the water slide. The marble passes through but doesn’t stop yet!

The Magic Rule: Lazy Evaluation

Streams are lazy! They don’t do ANY work until you ask for the final result.

// Nothing happens yet!
Stream<Integer> lazy = numbers.stream()
    .filter(n -> n > 2)
    .map(n -> n * 10);

// NOW it runs (collect is the trigger)
List<Integer> result = lazy.collect(
    Collectors.toList()
);

Think of it like ordering food:

  • You tell the waiter everything you want (intermediate)
  • Kitchen only starts cooking when you say β€œThat’s all!” (terminal)

🎯 filter Operation

filter is like a security guard. It only lets certain items pass!

Simple Example

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

// Keep only even numbers
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// Result: [2, 4, 6]

Real-Life Example

List<String> names = Arrays.asList(
    "Ana", "Bob", "Alice", "Ben"
);

// Names starting with 'A'
names.stream()
    .filter(name -> name.startsWith("A"))
    .forEach(System.out::println);
// Output: Ana, Alice
graph TD A["1, 2, 3, 4, 5, 6"] --> B{Is it Even?} B -->|Yes| C["βœ… Pass Through"] B -->|No| D["❌ Blocked"] C --> E["2, 4, 6"]

🎨 map Operation

map transforms each item. It’s like a painting station where every toy gets a new look!

Simple Example

List<Integer> numbers = Arrays.asList(1, 2, 3);

// Double each number
List<Integer> doubled = numbers.stream()
    .map(n -> n * 2)
    .collect(Collectors.toList());
// Result: [2, 4, 6]

String Transformation

List<String> names = Arrays.asList(
    "alice", "bob", "charlie"
);

// Make uppercase
List<String> upper = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// Result: [ALICE, BOB, CHARLIE]

Getting Object Properties

List<Person> people = Arrays.asList(
    new Person("Ana", 25),
    new Person("Bob", 30)
);

// Extract just the names
List<String> names = people.stream()
    .map(Person::getName)
    .collect(Collectors.toList());
// Result: [Ana, Bob]
graph TD A["alice, bob"] --> B["🎨 toUpperCase"] B --> C["ALICE, BOB"]

πŸŽͺ flatMap Operation

flatMap is special. Imagine you have boxes inside boxes. flatMap opens ALL boxes and puts everything on ONE line!

The Problem

List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
// [[1,2], [3,4], [5,6]]

The Solution: flatMap

List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// Result: [1, 2, 3, 4, 5, 6]

String Words Example

List<String> sentences = Arrays.asList(
    "Hello World",
    "Java Streams"
);

// Split into individual words
List<String> words = sentences.stream()
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .collect(Collectors.toList());
// Result: [Hello, World, Java, Streams]
graph TD A["[[1,2], [3,4]]"] --> B["flatMap"] B --> C["[1, 2, 3, 4]"] D["Nested boxes"] --> E["One flat line"]

πŸ“Š sorted and distinct Operations

sorted: Line Up Nicely!

List<Integer> nums = Arrays.asList(5, 2, 8, 1);

// Natural order
List<Integer> sorted = nums.stream()
    .sorted()
    .collect(Collectors.toList());
// Result: [1, 2, 5, 8]

// Reverse order
List<Integer> reverse = nums.stream()
    .sorted(Comparator.reverseOrder())
    .collect(Collectors.toList());
// Result: [8, 5, 2, 1]

distinct: No Duplicates!

List<Integer> dupes = Arrays.asList(
    1, 2, 2, 3, 3, 3, 4
);

List<Integer> unique = dupes.stream()
    .distinct()
    .collect(Collectors.toList());
// Result: [1, 2, 3, 4]

Combining Both

List<String> words = Arrays.asList(
    "banana", "apple", "apple", "cherry"
);

List<String> result = words.stream()
    .distinct()
    .sorted()
    .collect(Collectors.toList());
// Result: [apple, banana, cherry]
graph TD A["5, 2, 8, 1"] --> B["sorted"] B --> C["1, 2, 5, 8"] D["1, 2, 2, 3, 3"] --> E["distinct"] E --> F["1, 2, 3"]

βœ‚οΈ limit and skip Operations

limit: Take Only First N

List<Integer> nums = Arrays.asList(
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
);

// Take first 3
List<Integer> firstThree = nums.stream()
    .limit(3)
    .collect(Collectors.toList());
// Result: [1, 2, 3]

skip: Jump Over First N

// Skip first 3, take the rest
List<Integer> afterThree = nums.stream()
    .skip(3)
    .collect(Collectors.toList());
// Result: [4, 5, 6, 7, 8, 9, 10]

Pagination Example

int pageSize = 3;
int pageNumber = 2; // 0-indexed

List<Integer> page = nums.stream()
    .skip(pageNumber * pageSize)
    .limit(pageSize)
    .collect(Collectors.toList());
// Page 2 (items 7-9): [7, 8, 9]

Top N Pattern

List<Integer> scores = Arrays.asList(
    85, 92, 78, 95, 88, 76
);

// Top 3 scores
List<Integer> top3 = scores.stream()
    .sorted(Comparator.reverseOrder())
    .limit(3)
    .collect(Collectors.toList());
// Result: [95, 92, 88]
graph TD A["1,2,3,4,5,6,7,8,9,10"] --> B["limit 3"] B --> C["1, 2, 3"] A --> D["skip 3"] D --> E["4,5,6,7,8,9,10"]

🎯 Putting It All Together

Let’s build a real pipeline! Imagine we have students and want the top 3 passing grades:

List<Student> students = Arrays.asList(
    new Student("Ana", 85),
    new Student("Bob", 92),
    new Student("Cat", 45),
    new Student("Dan", 78),
    new Student("Eve", 95),
    new Student("Fay", 55)
);

List<String> topPassingStudents = students
    .stream()
    .filter(s -> s.getGrade() >= 60)
    .sorted((a, b) ->
        b.getGrade() - a.getGrade())
    .limit(3)
    .map(Student::getName)
    .collect(Collectors.toList());

// Result: [Eve, Bob, Ana]

What happened:

  1. 🎯 filter β†’ Kept only passing grades (β‰₯60)
  2. πŸ“Š sorted β†’ Highest grades first
  3. βœ‚οΈ limit β†’ Take top 3 only
  4. 🎨 map β†’ Get just the names
  5. πŸ“¦ collect β†’ Put into a List

πŸ’‘ Key Takeaways

Operation What It Does Analogy
stream() Start the flow Get on the slide
filter() Keep matching items Security guard
map() Transform each item Painting station
flatMap() Flatten nested data Unbox everything
sorted() Arrange in order Line up by height
distinct() Remove duplicates Unique tickets only
limit() Take first N VIP section
skip() Jump over N Skip the line

πŸš€ You Did It!

You now understand the Stream API basics! Streams make your code:

  • ✨ Cleaner - Less boilerplate
  • πŸ“– Readable - Flows like English
  • πŸš€ Powerful - Complex operations made simple

Remember: Streams are lazy water slides. They only run when you ask for the result. Now go make your data FLOW! 🌊

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.