Java Stream API 常用方法实践
Java Stream API 自 Java 8 引入以来,彻底改变了我们处理集合数据的方式。它提供了一种声明式、函数式的数据处理方式,使代码更加简洁、易读且高效。本文将详细介绍 Stream API 的常用方法,并通过实际示例展示其强大功能。
Stream API 基础
Stream 是一种可以让你以声明式方式处理数据序列的抽象概念。它不是数据结构,而是对数据源的一种视图,可以执行各种操作来转换或处理数据。
Stream 常用方法分类
1. 创建 Stream
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);
// 使用 Stream.of()
Stream<String> stream3 = Stream.of("a", "b", "c");
// 使用 Stream.builder()
Stream<String> stream4 = Stream.<String>builder()
.add("a")
.add("b")
.add("c")
.build();
// 无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);2. 中间操作(Intermediate Operations)
过滤操作
// filter - 过滤满足条件的元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 结果: [2, 4, 6]
// distinct - 去重
List<Integer> duplicates = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> unique = duplicates.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4]
// limit - 限制流的大小
List<Integer> limited = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 结果: [1, 2, 3]
// skip - 跳过前n个元素
List<Integer> skipped = numbers.stream()
.skip(3)
.collect(Collectors.toList());
// 结果: [4, 5, 6]映射操作
// map - 将每个元素转换为另一种形式
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果: [5, 6, 6]
// flatMap - 将每个元素映射为一个流,然后将所有流合并
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
List<String> flattened = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果: ["a", "b", "c", "d"]
// peek - 在流中每个元素被处理时执行操作(主要用于调试)
List<String> result = words.stream()
.peek(System.out::println)
.map(String::toUpperCase)
.collect(Collectors.toList());排序操作
// sorted - 自然排序
List<Integer> unsorted = Arrays.asList(3, 1, 4, 1, 5, 9);
List<Integer> sorted = unsorted.stream()
.sorted()
.collect(Collectors.toList());
// 结果: [1, 1, 3, 4, 5, 9]
// sorted - 自定义排序
List<String> names = Arrays.asList("John", "Sarah", "Mark", "Tina");
List<String> sortedByNameLength = names.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
// 结果: ["Mark", "John", "Sarah", "Tina"]截断操作(JDK 9+)
// takeWhile - 取满足条件的元素,直到条件不满足
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 1);
List<Integer> taken = numbers.stream()
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
// 结果: [1, 2, 3]
// dropWhile - 丢弃满足条件的元素,直到条件不满足
List<Integer> dropped = numbers.stream()
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
// 结果: [4, 5, 1]3. 终端操作(Terminal Operations)
匹配操作
// anyMatch - 检查是否至少有一个元素满足条件
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0);
// 结果: true
// allMatch - 检查是否所有元素都满足条件
boolean allPositive = numbers.stream().allMatch(n -> n > 0);
// 结果: true
// noneMatch - 检查是否没有元素满足条件
boolean noNegative = numbers.stream().noneMatch(n -> n < 0);
// 结果: true查找操作
// findFirst - 返回第一个元素(Optional)
Optional<Integer> first = numbers.stream().findFirst();
// 结果: Optional[1]
// findAny - 返回任意一个元素(Optional)
Optional<Integer> any = numbers.stream().findAny();
// 结果: Optional[某个元素]统计操作
// count - 计算元素数量
long count = numbers.stream().count();
// 结果: 6
// min - 找到最小值
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
// 结果: Optional[1]
// max - 找到最大值
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
// 结果: Optional[9]归约操作
// reduce - 将流归约为一个值
int sum = numbers.stream().reduce(0, Integer::sum);
// 结果: 24
// reduce - 带初始值的归约
int product = numbers.stream().reduce(1, (a, b) -> a * b);
// 结果: 1080
// reduce - 无初始值的归约(返回Optional)
Optional<Integer> sumOptional = numbers.stream().reduce(Integer::sum);收集操作
// collect - 收集到集合
List<Integer> collectedList = numbers.stream().collect(Collectors.toList());
Set<Integer> collectedSet = numbers.stream().collect(Collectors.toSet());
Map<Integer, String> collectedMap = numbers.stream()
.collect(Collectors.toMap(n -> n, n -> "Number " + n));
// collect - 自定义收集器
String joined = numbers.stream()
.map(String::valueOf)
.collect(Collectors.joining(", "));
// 结果: "1, 2, 3, 4, 5, 9"
// collect - 分组
Map<Boolean, List<Integer>> grouped = numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2 == 0));
// 结果: {false=[1, 3, 5, 9], true=[2, 4]}
// collect - 分区
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n > 3));
// 结果: {false=[1, 2, 3], true=[4, 5, 9]}并行流
// parallelStream - 并行处理
List<Integer> parallelResult = numbers.parallelStream()
.map(n -> n * 2)
.collect(Collectors.toList());实际应用示例
示例1:处理用户数据
class User {
private String name;
private int age;
private String city;
// 构造函数、getter和setter
}
List<User> users = // 用户列表
// 找出所有成年用户
List<User> adults = users.stream()
.filter(user -> user.getAge() >= 18)
.collect(Collectors.toList());
// 按城市分组统计用户数量
Map<String, Long> cityCount = users.stream()
.collect(Collectors.groupingBy(User::getCity, Collectors.counting()));
// 计算平均年龄
double averageAge = users.stream()
.mapToInt(User::getAge)
.average()
.orElse(0);示例2:处理日志数据
List<LogEntry> logs = // 日志条目列表
// 找出错误日志
List<LogEntry> errors = logs.stream()
.filter(log -> log.getLevel().equals("ERROR"))
.collect(Collectors.toList());
// 按日期分组统计错误数量
Map<LocalDate, Long> errorCountByDate = logs.stream()
.filter(log -> log.getLevel().equals("ERROR"))
.collect(Collectors.groupingBy(
log -> log.getTimestamp().toLocalDate(),
Collectors.counting()
));
// 找出最频繁的错误类型
Optional<Map.Entry<String, Long>> mostFrequentError = logs.stream()
.filter(log -> log.getLevel().equals("ERROR"))
.collect(Collectors.groupingBy(LogEntry::getMessage, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue());示例3:处理商品数据
class Product {
private String name;
private double price;
private String category;
// 构造函数、getter和setter
}
List<Product> products = // 商品列表
// 计算总价格
double total = products.stream()
.mapToDouble(Product::getPrice)
.sum();
// 按类别分组并计算每个类别的平均价格
Map<String, Double> avgPriceByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.averagingDouble(Product::getPrice)
));
// 找出最贵的商品
Optional<Product> mostExpensive = products.stream()
.max(Comparator.comparingDouble(Product::getPrice));性能考虑
1. 并行流 vs 顺序流:对于大型数据集,并行流可以提高性能,但对于小型数据集,顺序流可能更快。
2. 中间操作的延迟执行:Stream 操作是延迟执行的,只有在终端操作时才会实际处理数据。
3. 避免不必要的装箱/拆箱:使用 mapToInt、mapToLong、mapToDouble 等专门的方法来避免装箱开销。
JDK 版本特性
如文档所述,不同 JDK 版本对 Stream API 的支持有所不同:
JDK 11:引入了
takeWhile()、dropWhile()和Stream.ofNullable()JDK 17:引入了
Stream.toList()简化收集操作JDK 21:可能包含更多优化和新特性
总结
Java Stream API 提供了强大而灵活的数据处理能力,通过函数式编程风格使代码更加简洁和可读。掌握 Stream API 的各种方法可以显著提高你的 Java 编程效率和代码质量。
希望本文的示例能帮助你更好地理解和使用 Java Stream API!如有任何问题或建议,欢迎在评论区讨论。
评论区