Stream API — map, filter, reduce, collect
Stream API — data-কে command-এর বদলে describe করে processing
1. Declarative, Lazy, Maybe Parallel
A Stream is a pipeline over a source (collection, array, file, generator). You
describe the transformations — map, filter, sorted — and Java runs
them lazily, combining steps, only when a terminal operation like collect or
reduce is invoked.
map, filter, sorted), শেষে terminal operation (collect, reduce)। Intermediate step lazy — terminal না আসা পর্যন্ত কিছু চলে না।
2. map and filter
filter removes elements that don't match a Predicate; map
transforms each element via a Function.
filter match না করা element-গুলো সরিয়ে দেয়; map প্রতিটি element-কে Function দিয়ে রূপান্তর করে।
import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
List<String> cities = List.of("Dhaka", "Chattogram", "Sylhet", "Rajshahi", "Khulna");
List<String> bigUpper = cities.stream()
.filter(c -> c.length() > 6)
.map(String::toUpperCase)
.toList();
System.out.println(bigUpper);
}
}
3. reduce — Folding a Stream into a Value
reduce collapses a stream into a single value using an associative binary operator. Common
reductions (sum, max, count) have named shortcuts on primitive
streams.
reduce একটি associative binary operator দিয়ে stream-কে এক-value-তে পরিণত করে। sum/max/count-এর মতো সাধারণ reduction-এ primitive stream-এ shortcut আছে।
import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
List<Integer> xs = List.of(1, 2, 3, 4, 5);
int sum = xs.stream().mapToInt(Integer::intValue).sum();
int prod = xs.stream().reduce(1, (a, b) -> a * b);
int max = xs.stream().mapToInt(Integer::intValue).max().orElse(Integer.MIN_VALUE);
System.out.println("sum = " + sum);
System.out.println("prod = " + prod);
System.out.println("max = " + max);
}
}
4. collect and Collectors
collect turns a stream into a concrete data structure. The Collectors factory
gives you toList, toSet, toMap, joining,
groupingBy, partitioningBy, and more.
collect stream-কে একটি concrete data structure-এ ফিরিয়ে দেয়। Collectors-এ toList, toSet, toMap, joining, groupingBy, partitioningBy-সহ অনেক কিছু আছে।
import java.util.*;
import java.util.stream.*;
class Main {
record Student(String name, String dept, int marks) {}
public static void main(String[] args) {
List<Student> xs = List.of(
new Student("Arif", "CSE", 88),
new Student("Rina", "CSE", 92),
new Student("Hasan", "EEE", 76),
new Student("Maya", "EEE", 81),
new Student("Nabil", "CSE", 65)
);
// joining
String names = xs.stream().map(Student::name).collect(Collectors.joining(", "));
System.out.println("names = " + names);
// groupingBy dept -> list of students
Map<String, List<Student>> byDept =
xs.stream().collect(Collectors.groupingBy(Student::dept));
byDept.forEach((k, v) -> System.out.println(k + " -> " + v.size()));
// averagingInt per dept
Map<String, Double> avg =
xs.stream().collect(Collectors.groupingBy(Student::dept, Collectors.averagingInt(Student::marks)));
System.out.println("avg marks by dept = " + avg);
// partitioningBy passed/failed (>= 70)
Map<Boolean, List<Student>> passed =
xs.stream().collect(Collectors.partitioningBy(s -> s.marks() >= 70));
System.out.println("passed: " + passed.get(true).size());
System.out.println("failed: " + passed.get(false).size());
}
}
5. Laziness & Parallel Streams
Intermediate operations are lazy — nothing happens until a terminal op runs. Short-circuit
operations like findFirst, anyMatch, limit stop as soon as they can.
parallelStream() spreads work across the common fork-join pool — great for CPU-bound
workloads on large data, but not a free win.
findFirst, anyMatch, limit short-circuit — যত দ্রুত possible তত দ্রুত থেমে যায়। parallelStream() কাজ multiple core-এ ছড়িয়ে দেয় — বড় CPU-bound কাজে কাজে লাগে, কিন্তু সব সময় দ্রুত নয়।
import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Lazy + short-circuit: find first even greater than 100
int found = IntStream.rangeClosed(1, 1_000_000)
.filter(n -> n > 100 && n % 2 == 0)
.findFirst()
.orElse(-1);
System.out.println("found = " + found);
// Parallel: sum 1..1_000_000
long sum = IntStream.rangeClosed(1, 1_000_000).parallel().sum();
System.out.println("parallel sum = " + sum);
}
}
6. Vocabulary
| Term | Meaning | বাংলায় |
|---|---|---|
| Stream | Lazy pipeline over a source. | source-এর উপর lazy pipeline। |
| Intermediate op | map, filter, sorted — lazy, returns a Stream. | Lazy; আরেকটি Stream দেয়। |
| Terminal op | collect, reduce, forEach — triggers execution. | এটি এলেই সব চলে। |
| Short-circuit | Stops as soon as possible (anyMatch, limit). | যত দ্রুত সম্ভব থামে। |
Collectors | Factory of terminal collectors. | terminal collector-এর factory। |
parallelStream | Uses common fork-join pool. | Fork-join pool-এ parallel execution। |
IllegalStateException.
Stream single-use — terminal op-এর পর close; আবার ব্যবহার করলে
IllegalStateException।
7. Practice Problems
-
Sum all even numbers from 1 to 100 using a stream.1 থেকে 100-এর মধ্যে সব জোড় সংখ্যার যোগফল stream দিয়ে বের করুন।
✨ Show Answer
Main.javaimport java.util.stream.*; class Main { public static void main(String[] args) { int sum = IntStream.rangeClosed(1, 100) .filter(n -> n % 2 == 0).sum(); System.out.println(sum); } } -
Given a list of words, return a new list of their uppercased versions longer than 4 characters.কিছু শব্দের list থেকে 4-এর চেয়ে বড় উপরকেস version-এর নতুন list বের করুন।
✨ Show Answer
Main.javaimport java.util.*; class Main { public static void main(String[] args) { List<String> xs = List.of("go", "hello", "dhaka", "hi", "chattogram"); List<String> out = xs.stream() .filter(s -> s.length() > 4) .map(String::toUpperCase) .toList(); System.out.println(out); } } -
Group a list of words by their first letter using
Collectors.groupingBy.কিছু শব্দকে প্রথম অক্ষরের ভিত্তিতেgroupingByদিয়ে group করুন।✨ Show Answer
Main.javaimport java.util.*; import java.util.stream.*; class Main { public static void main(String[] args) { List<String> xs = List.of("apple", "ant", "banana", "berry", "cherry"); Map<Character, List<String>> g = xs.stream().collect(Collectors.groupingBy(s -> s.charAt(0))); System.out.println(g); } } -
Explain in your words what "lazy evaluation" means for streams.Stream-এ "lazy evaluation" বলতে কী বোঝায় — নিজের ভাষায় লিখুন।
✨ Show Answer
Answer: Intermediate operations on a stream record what to do but don't actually do it. The whole pipeline starts running only when a terminal operation asks for a result, and the JVM fuses the steps so it usually passes each element through the whole pipeline once. This enables short-circuit ops (
findFirst,limit) to skip unnecessary work on huge datasets.Intermediate operation শুধু "কী করতে হবে" record করে, চালায় না। Terminal operation আসলেই পুরো pipeline চালু হয়; JVM step-গুলো fuse করে, সাধারণত প্রতি element এক-বারই পুরো pipeline দিয়ে যায়। এর ফলে
findFirst/limit-এর মতো short-circuit op বিশাল dataset-এ অপ্রয়োজনীয় কাজ এড়িয়ে যেতে পারে। -
From a list of
Student(name, marks), compute the name of the top scorer with a stream.কিছুStudent(name, marks)থেকে সর্বোচ্চ নম্বর পাওয়া ছাত্রের নাম বের করুন।✨ Show Answer
Main.javaimport java.util.*; class Main { record Student(String name, int marks) {} public static void main(String[] args) { List<Student> xs = List.of( new Student("Arif", 88), new Student("Rina", 92), new Student("Hasan", 76) ); String top = xs.stream() .max(Comparator.comparingInt(Student::marks)) .map(Student::name).orElse("?"); System.out.println("top = " + top); } }
Summary — Module 29
A Stream is a lazy pipeline: source → intermediate ops (map, filter,
sorted) → terminal op (collect, reduce, forEach).
Collectors gives you toList, toMap, groupingBy,
partitioningBy, and joining. Use parallelStream() only for large,
CPU-bound, non-I/O-bound workloads.
Collectors-এ toList, toMap, groupingBy, partitioningBy, joining আছে। বড় CPU-bound কাজেই parallelStream() বেছে নিন।