跳到主要内容

Java Stream API

Stream API 是 Java 8 引入的强大特性,用于处理集合数据。理解 Stream API 的使用是进行函数式编程的关键。本章将详细介绍 Java 中的 Stream API。

Stream 流概念

什么是 Stream

Stream是一个数据流,用于对集合进行函数式操作。

特点

  • 不存储数据:Stream 不存储元素
  • 函数式编程:支持函数式操作
  • 惰性求值:中间操作是惰性的
  • 可消费性:只能消费一次

Stream 的创建

import java.util.*;
import java.util.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.generate
Stream<String> stream4 = Stream.generate(() -> "hello").limit(5);

// 使用 Stream.iterate
Stream<Integer> stream5 = Stream.iterate(0, n -> n + 2).limit(10);

// 从文件创建
Stream<String> lines = Files.lines(Paths.get("file.txt"));

filter / map / reduce / collect

filter(过滤)

**filter**用于过滤元素。

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

// 过滤偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 结果:[2, 4, 6, 8, 10]

// 过滤大于 5 的数
List<Integer> greater = numbers.stream()
.filter(n -> n > 5)
.collect(Collectors.toList());
// 结果:[6, 7, 8, 9, 10]

map(映射)

**map**用于转换元素。

List<String> names = Arrays.asList("张三", "李四", "王五");

// 转换为大写
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 结果:["张三", "李四", "王五"]

// 获取长度
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果:[2, 2, 2]

reduce(归约)

**reduce**用于将流归约为一个值。

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

// 求和
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
// 结果:Optional[15]

// 求和(带初始值)
Integer sum2 = numbers.stream()
.reduce(0, (a, b) -> a + b);
// 结果:15

// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
// 结果:Optional[5]

collect(收集)

**collect**用于将流收集到集合中。

import java.util.stream.Collectors;

List<String> names = Arrays.asList("张三", "李四", "王五");

// 收集到 List
List<String> list = names.stream()
.collect(Collectors.toList());

// 收集到 Set
Set<String> set = names.stream()
.collect(Collectors.toSet());

// 收集到 Map
Map<String, Integer> map = names.stream()
.collect(Collectors.toMap(
name -> name,
String::length
));
// 结果:{"张三": 2, "李四": 2, "王五": 2}

并行流示例

并行流

并行流利用多核处理器并行处理数据。

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

// 顺序流
long start1 = System.currentTimeMillis();
int sum1 = numbers.stream()
.mapToInt(n -> n * n)
.sum();
long end1 = System.currentTimeMillis();
System.out.println("顺序流耗时:" + (end1 - start1) + "ms");

// 并行流
long start2 = System.currentTimeMillis();
int sum2 = numbers.parallelStream()
.mapToInt(n -> n * n)
.sum();
long end2 = System.currentTimeMillis();
System.out.println("并行流耗时:" + (end2 - start2) + "ms");

并行流的注意事项

// ⚠️ 注意:并行流不保证顺序
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 顺序流:保证顺序
numbers.stream()
.forEachOrdered(System.out::println);
// 输出:1, 2, 3, 4, 5

// 并行流:不保证顺序
numbers.parallelStream()
.forEach(System.out::println);
// 输出顺序可能不同

Stream 的其他操作

distinct(去重)

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

List<Integer> distinct = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5]

sorted(排序)

List<String> names = Arrays.asList("王五", "张三", "李四");

// 自然排序
List<String> sorted = names.stream()
.sorted()
.collect(Collectors.toList());
// 结果:["李四", "王五", "张三"]

// 自定义排序
List<String> customSorted = names.stream()
.sorted((a, b) -> b.compareTo(a)) // 降序
.collect(Collectors.toList());

limit 和 skip

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

// 取前 5 个
List<Integer> first5 = numbers.stream()
.limit(5)
.collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5]

// 跳过前 5 个
List<Integer> skip5 = numbers.stream()
.skip(5)
.collect(Collectors.toList());
// 结果:[6, 7, 8, 9, 10]

flatMap(扁平化)

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

// 扁平化
List<Integer> flat = lists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

终端操作

List<String> names = Arrays.asList("张三", "李四", "王五");

// forEach:遍历
names.stream().forEach(System.out::println);

// count:计数
long count = names.stream().count(); // 3

// anyMatch:任意匹配
boolean hasZhang = names.stream()
.anyMatch(s -> s.startsWith("张")); // true

// allMatch:全部匹配
boolean allLength2 = names.stream()
.allMatch(s -> s.length() == 2); // true

// noneMatch:无匹配
boolean noZhao = names.stream()
.noneMatch(s -> s.startsWith("赵")); // true

// findFirst:查找第一个
Optional<String> first = names.stream()
.findFirst(); // Optional["张三"]

// findAny:查找任意一个
Optional<String> any = names.stream()
.findAny();

实际示例

示例 1:数据处理

import java.util.*;
import java.util.stream.Collectors;

List<Person> persons = Arrays.asList(
new Person("张三", 25, "男"),
new Person("李四", 20, "女"),
new Person("王五", 30, "男"),
new Person("赵六", 18, "女")
);

// 过滤年龄大于 20 的人
List<Person> adults = persons.stream()
.filter(p -> p.getAge() > 20)
.collect(Collectors.toList());

// 获取所有姓名
List<String> names = persons.stream()
.map(Person::getName)
.collect(Collectors.toList());

// 按年龄分组
Map<Integer, List<Person>> byAge = persons.stream()
.collect(Collectors.groupingBy(Person::getAge));

// 按性别分组
Map<String, List<Person>> byGender = persons.stream()
.collect(Collectors.groupingBy(Person::getGender));

示例 2:统计操作

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

// 求和
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum(); // 55

// 平均值
OptionalDouble avg = numbers.stream()
.mapToInt(Integer::intValue)
.average(); // OptionalDouble[5.5]

// 最大值
OptionalInt max = numbers.stream()
.mapToInt(Integer::intValue)
.max(); // OptionalInt[10]

// 最小值
OptionalInt min = numbers.stream()
.mapToInt(Integer::intValue)
.min(); // OptionalInt[1]

// 统计信息
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
// stats.getCount() = 10
// stats.getSum() = 55
// stats.getAverage() = 5.5
// stats.getMax() = 10
// stats.getMin() = 1

示例 3:字符串处理

List<String> words = Arrays.asList("hello", "world", "java", "stream");

// 转换为大写并连接
String result = words.stream()
.map(String::toUpperCase)
.collect(Collectors.joining(", "));
// 结果:"HELLO, WORLD, JAVA, STREAM"

// 过滤长度大于 4 的单词
List<String> longWords = words.stream()
.filter(s -> s.length() > 4)
.collect(Collectors.toList());
// 结果:["hello", "world", "stream"]

Stream 的最佳实践

1. 使用链式调用

// ✅ 推荐:链式调用
List<String> result = list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());

2. 避免副作用

// ❌ 不推荐:在 Stream 中产生副作用
List<String> result = new ArrayList<>();
list.stream()
.filter(s -> s.length() > 3)
.forEach(result::add);

// ✅ 推荐:使用 collect
List<String> result2 = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());

3. 合理使用并行流

// 大数据量时使用并行流
List<Integer> largeList = // ... 大量数据
List<Integer> result = largeList.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());

小结

Java Stream API 要点:

  • Stream 概念:数据流,函数式操作
  • filter:过滤元素
  • map:转换元素
  • reduce:归约为一个值
  • collect:收集到集合
  • 并行流:利用多核并行处理

关键要点

  • Stream 不存储数据,只能消费一次
  • 中间操作是惰性的
  • 使用链式调用处理数据
  • 合理使用并行流提高性能
  • 避免在 Stream 中产生副作用

理解了 Stream API,你就能高效地处理集合数据。在下一章,我们将学习 Java Optional 的使用。