Master Java Stream API: filter, map, collect in One Line
This article introduces Java 8's Stream API, explaining its lazy, declarative processing model, how to create streams from collections or arrays, and demonstrates core intermediate and terminal operations with concrete code examples and a practical employee‑salary use case.
Introduction
Before Java 8, filtering, transforming, and aggregating collections required verbose loops and conditionals, making code hard to maintain. The Stream API offers a functional, declarative way to handle collections, turning complex data operations into elegant chained calls.
Stream Processing Flow
Data source (Collection/Array)
↓
Intermediate operations (lazy, chainable)
filter → map → sorted → distinct → limit …
↓
Terminal operations (trigger execution, can be called only once)
collect → forEach → count → reduce → findFirst …Stream is lazy : intermediate operations do not execute immediately; execution is triggered only when a terminal operation is invoked.
Creating Streams
import java.util.stream.*;
import java.util.*;
// From a collection
List<String> list = List.of("Java", "Python", "Go");
Stream<String> s1 = list.stream();
// From an array
int[] arr = {1, 2, 3, 4, 5};
IntStream s2 = Arrays.stream(arr);
// Direct creation
Stream<String> s3 = Stream.of("a", "b", "c");
// Infinite streams
Stream<Integer> s4 = Stream.iterate(0, n -> n + 2).limit(5); // 0,2,4,6,8
Stream<Double> s5 = Stream.generate(Math::random).limit(3);Common Intermediate Operations
filter (filtering)
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evens = nums.stream()
.filter(n -> n % 2 == 0) // keep only even numbers
.collect(Collectors.toList());
System.out.println(evens); // [2, 4, 6, 8, 10]map (transformation)
List<String> names = List.of("alice", "bob", "charlie");
// Upper‑case strings
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper); // [ALICE, BOB, CHARLIE]
// Map strings to their lengths
List<String> langs = List.of("Java", "Python", "Go");
List<Integer> lengths = langs.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); // [4, 6, 2]sorted (sorting)
List<Integer> nums = List.of(5, 3, 8, 1, 9, 2);
// Natural order
List<Integer> sorted = nums.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sorted); // [1, 2, 3, 5, 8, 9]
// Custom order (descending)
List<Integer> desc = nums.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(desc); // [9, 8, 5, 3, 2, 1]distinct / limit / skip
List<Integer> nums = List.of(1, 2, 2, 3, 3, 3, 4, 5);
nums.stream().distinct().forEach(n -> System.out.print(n + " ")); // 1 2 3 4 5
List<Integer> first3 = nums.stream().limit(3).collect(Collectors.toList()); // [1, 2, 2]
List<Integer> skip3 = nums.stream().skip(3).collect(Collectors.toList()); // [3, 3, 4, 5]Terminal Operations
collect (gathering)
import java.util.stream.Collectors;
List<String> langs = List.of("Java", "Python", "Go", "Java", "Rust");
// To List
List<String> list = langs.stream().collect(Collectors.toList());
// To Set (deduplication)
Set<String> set = langs.stream().collect(Collectors.toSet());
// To Map
Map<String, Integer> map = langs.stream()
.distinct()
.collect(Collectors.toMap(s -> s, String::length));
System.out.println(map); // {Java=4, Python=6, Go=2, Rust=4}
// Grouping and counting (frequency)
Map<String, Long> freq = langs.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
System.out.println(freq); // {Java=2, Python=1, Go=1, Rust=1}
// Joining strings
String joined = langs.stream()
.distinct()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(joined); // [Java, Python, Go, Rust]count / findFirst / anyMatch
List<String> langs = List.of("Java", "Python", "Go", "JavaScript");
long count = langs.stream().filter(s -> s.startsWith("J")).count();
System.out.println(count); // 2
Optional<String> first = langs.stream().filter(s -> s.length() > 4).findFirst();
first.ifPresent(System.out::println); // Python
boolean anyMatch = langs.stream().anyMatch(s -> s.equals("Go")); // true
boolean allMatch = langs.stream().allMatch(s -> s.length() > 1); // true
boolean noneMatch = langs.stream().noneMatch(s -> s.isEmpty()); // truereduce (aggregation)
List<Integer> nums = List.of(1, 2, 3, 4, 5);
// Sum
int sum = nums.stream().reduce(0, Integer::sum);
System.out.println(sum); // 15
// Max value
Optional<Integer> max = nums.stream().reduce(Integer::max);
max.ifPresent(System.out::println); // 5Comprehensive Practice
import java.util.*;
import java.util.stream.*;
record Employee(String name, String dept, double salary) {}
public class StreamPractice {
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("张三", "研发", 18000),
new Employee("李四", "研发", 22000),
new Employee("王五", "市场", 15000),
new Employee("赵六", "市场", 17000),
new Employee("钱七", "研发", 25000)
);
// Requirement 1: Names of R&D staff with salary > 20000
List<String> devHighSalary = employees.stream()
.filter(e -> "研发".equals(e.dept()))
.filter(e -> e.salary() > 20000)
.map(Employee::name)
.collect(Collectors.toList());
System.out.println("高薪研发:" + devHighSalary); // [李四, 钱七]
// Requirement 2: Average salary per department
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::dept,
Collectors.averagingDouble(Employee::salary)
));
avgSalaryByDept.forEach((dept, avg) ->
System.out.printf("%s 平均薪资:%.0f%n", dept, avg));
// Requirement 3: Top 3 salaries descending
employees.stream()
.sorted(Comparator.comparingDouble(Employee::salary).reversed())
.limit(3)
.forEach(e -> System.out.println(e.name() + ": " + e.salary()));
}
}Common Pitfalls
Stream reuse : a Stream can be used only once; after a terminal operation the Stream is closed.
Empty streams : findFirst() returns an Optional; avoid calling get() without checking.
Modifying the source collection : Stream operations do not alter the original collection; collect the results with collect.
Parallel streams : parallelStream() runs in multiple threads; ensure thread‑safety and be aware of ordering.
Learning Summary
Intermediate ops → filter, map, sorted, distinct, limit
Terminal ops → collect, count, forEach, reduce
Collectors → toList, toSet, toMap, groupingBy, joining
Principle → declarative description of "what to do", not "how to do it"Stream API upgrades collection handling from "imperative" to "declarative", making code shorter and intent clearer!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
