0% found this document useful (0 votes)
4 views7 pages

Streams API - Basic Understanding

The document explains the Streams API in Java, likening it to a factory conveyor belt where data is processed through various stages such as filtering, transforming, and sorting. It provides examples using an Employee class and demonstrates methods like filter, map, and sorted to manipulate lists of employees. Additionally, it introduces terminal operations and a framework for approaching stream-related problems, along with challenges for practice.

Uploaded by

MJ
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views7 pages

Streams API - Basic Understanding

The document explains the Streams API in Java, likening it to a factory conveyor belt where data is processed through various stages such as filtering, transforming, and sorting. It provides examples using an Employee class and demonstrates methods like filter, map, and sorted to manipulate lists of employees. Additionally, it introduces terminal operations and a framework for approaching stream-related problems, along with challenges for practice.

Uploaded by

MJ
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Streams API

Forget methods for a second. Think of a stream like a factory conveyor belt:

Raw materials → [Station 1] → [Station 2] → [Station 3] → Final Product


(your list) (filter) (transform) (sort) (collect)

Every station either:

●​ Removes items (filter, distinct, limit)


●​ Transforms items (map, flatMap)
●​ Reorders items (sorted)
●​ Terminates the belt and packages the result (collect, findFirst, count, reduce)

🏗️ The Blueprint — Always This Shape


result = source
.stream() // START the belt
// INTERMEDIATE — transforms items, returns a new stream
.filter(...) // keeps/removes items
.map(...) // shape-shifts each item
.sorted(...) // reorders
// TERMINAL — ends the belt, gives you your result
.collect(...); // package into List/Map/Set

👥 Our Dataset — We'll use this throughout


// Let's create a realistic Employee class
record Employee(String name, String dept, int salary, int age) {}

List<Employee> employees = [Link](


new Employee("Alice", "Engineering", 95000, 30),
new Employee("Bob", "Engineering", 72000, 45),
new Employee("Carol", "HR", 60000, 35),
new Employee("David", "HR", 65000, 28),
new Employee("Eve", "Engineering", 110000, 38),
new Employee("Frank", "Marketing", 55000, 25),
new Employee("Grace", "Marketing", 70000, 32)
);

🎯 Method 1: filter — The Gatekeeper


One question to ask yourself: "Do I want ALL items or just SOME?" If SOME → use filter.

// ❓ Problem: Get only Engineering employees


List<Employee> engineers = [Link]()
.filter(e -> [Link]().equals("Engineering")) // only let Engineers through
.collect([Link]());

// 👉 engineers = [Alice, Bob, Eve]


The lambda e -> [Link]().equals("Engineering") reads as:

"For each employee e, keep them IF this is true"

🎯 Method 2: map — The Shape-Shifter


One question: "Do I need the whole object, or just ONE thing from it?" If just ONE thing → use
map.

// ❓ Problem: Get just the names of all employees


List<String> names = [Link]()
.map(e -> [Link]()) // transform Employee → String
.collect([Link]());

// 👉 names = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace"]


Think of map as: "Replace each item with something else"

[Employee] [Employee] [Employee]


↓ ↓ ↓
[String] [String] [String]
🔗 Chaining — The Real Power
Now combine them. Read it top to bottom like a sentence:

// ❓ Problem: Get names of all Engineering employees, sorted alphabetically


List<String> result = [Link]()
.filter(e -> [Link]().equals("Engineering")) // keep only Engineers
.map(e -> [Link]()) // grab just their names
.sorted() // sort A→Z
.collect([Link]());

// 👉 ["Alice", "Bob", "Eve"]


The thinking process:

1.​ What's my input? → List of Employees


2.​ Do I want all or some? → Some (Engineering only) → filter
3.​ Do I need the whole object or part of it? → Just names → map
4.​ Any ordering? → Yes, alphabetical → sorted
5.​ What's my output container? → List → collect(toList())

🎯 Method 3: sorted — The Organizer


// ❓ Problem: Get all employees sorted by salary (highest first)
List<Employee> sorted = [Link]()
.sorted([Link](Employee::salary).reversed())
.collect([Link]());

// 👉 [Eve(110k), Alice(95k), Bob(72k), Grace(70k), David(65k), Carol(60k), Frank(55k)]

🎯 Terminal Methods — Packaging the Result


// collect(toList()) → gives you a List
// collect(toSet()) → gives you a Set (no duplicates)
// count() → gives you a long (how many)
// findFirst() → gives you Optional<T> (first match)
// anyMatch() → gives you boolean (does ANY match?)
// allMatch() → gives you boolean (do ALL match?)

//❓ Does any employee earn over 100k?


boolean hasHighEarner = [Link]()
.anyMatch(e -> [Link]() > 100000); // true (Eve earns 110k)

//❓ How many employees are in HR?


long hrCount = [Link]()
.filter(e -> [Link]().equals("HR"))
.count(); // 2

🏆 The "Think in 5 Questions" Framework


Before writing a single line, ask yourself:

# Question → Method

1 Do I want ALL items or SOME? filter if SOME

2 Do I need the whole object or a PART? map if PART

3 Do I need it ORDERED? sorted

4 Are there DUPLICATES I want to remove? distinct

5 What should my OUTPUT look like? collect / count /


findFirst

🔥 Now You Try — 3 Challenges


Try solving these mentally first, then code them:

Challenge 1 (Easy): Get a list of salaries of all Marketing employees.

Challenge 2 (Medium): Get the name of the youngest employee in Engineering.


Challenge 3 (Hard): Group all employees by department (gives you a Map<String,
List<Employee>>).

Want me to:

●​ Walk through the challenges together step by step?


●​ Go deeper into flatMap, reduce, or groupingBy?
●​ Show you the answers to the challenges with full explanation?

Tell me where you want to go next! 🚀

Great question! Let me break every part down:

.mapToInt(Integer::intValue)

Your stream starts as Stream<Integer> (boxed objects). mapToInt() converts it into an


IntStream (raw primitives).

Stream<Integer> → .mapToInt(Integer::intValue) → IntStream

Integer::intValue is a method reference that simply unwraps each Integer object into a
plain int. You could also write it as:

.mapToInt(n -> n) // autoboxing does the same thing

Why bother converting? Because IntStream gives you access to math-specific methods like
sum(), average(), min(), max() — which a regular Stream<Integer> does NOT have.

.summaryStatistics()

This is a special terminal operation only available on primitive streams (IntStream,


LongStream, DoubleStream). It computes all five statistics in a single pass over the data
and bundles them into one object — instead of you having to iterate the list 5 separate times.
IntSummaryStatistics stats = ...summaryStatistics();

The IntSummaryStatistics object holds:

Method Returns

[Link]() sum of all elements

[Link]() smallest element

[Link]() largest element

[Link] returns double


ge()

[Link] how many


() elements

Why not just do each separately?

You could, but it's 5 stream passes:

int sum = [Link]().mapToInt(Integer::intValue).sum();


int min = [Link]().mapToInt(Integer::intValue).min().getAsInt();
int max = [Link]().mapToInt(Integer::intValue).max().getAsInt();
double avg = [Link]().mapToInt(Integer::intValue).average().getAsDouble();
long count = [Link]().count();

summaryStatistics() does all of this in one single pass — much more efficient.

The full flow:

List<Integer> [3, 7, 1, 9, 4, 6]
|
.stream()
|
Stream<Integer>
|
.mapToInt(Integer::intValue)
|
IntStream (primitives: 3, 7, 1, 9, 4, 6)
|
.summaryStatistics()
|
IntSummaryStatistics
├── count = 6
├── sum = 30
├── min = 1
├── max = 9
└── average = 5.0

One thing to note about getAverage() — it returns a double, not an int, because
averages are rarely whole numbers. And if the stream were empty, getMin() and getMax()
would return Integer.MAX_VALUE and Integer.MIN_VALUE respectively, while
getAverage() would return 0.0 — so always be careful with empty streams.

You might also like