Java基础 - Java函数式编程 - Stream API
Introduction
Stream API
Stream API的特性
- 声明式: 你只需描述做什么,而无需关心如何做
- 可组合: 中间操作返回一个新的Stream,因此可以将多个操作链接起来
- 一次性消费: 一个Stream只能被一个终端操作消费一次
- 支持并行化
Stream的创建
stream的创建:在使用Stream API之前,你必须先获得一个Stream实例。Collection接口提供了stream()
和parallelStream()
默认方法。
1 |
|
Stream的中间操作
中间操作接收一个Stream,返回一个新的Stream。
filter(Predicate<T> predicate)
: 过滤。保留使predicate返回true的元素。1
2// 筛选出长度大于5的字符串
stream.filter(s -> s.length() > 5);map(Function<T, R> mapper)
: 映射/转换。将每个元素T转换为一个新的元素R。1
2// 将字符串列表转换为其长度的列表
stream.map(String::length); // 返回一个 Stream<Integer>peek(Consumer<T> action)
: 主要用于调试,它会对流中的每个元素执行一个操作,但不会改变流本身。1
stream.peek(s -> System.out.println("Processing: " + s));
sorted()
/sorted(Comparator<T> comparator)
: 排序。limit(long maxSize)
: 截断流,使其元素数量不超过maxSize。distinct()
: 去重。基于元素的equals()方法。
Stream的终端操作
终端操作是流水线的终点。它会触发所有惰性中间操作的执行,并产生一个最终结果或副作用(打印到控制台,调用某个服务的API等)。
惰性求值:中间操作不会立即执行。它们只是在流水线上注册了一个待执行的操作。只有当一个终端操作被调用时,整个流水线才会从源头开始,依次执行所有中间操作。
1 |
|
消费与副作用:对每个元素执行操作
void forEach(Consumer<? super T> action)
1
2
3
4
5List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(s -> s.startsWith("A"))
.forEach(System.out::println); // 副作用:打印到控制台
// 输出: Alice
查询与匹配:检查元素属性
Optional<T> findFirst()
Optional<T> findAny()
boolean anyMatch(Predicate<? super T> predicate)
, e.g.1
users.stream().anyMatch(user -> user.isAdmin())
boolean allMatch(Predicate<? super T> predicate)
归约:将流合并为单个值
比如,计算总和、求最大值、或将所有字符串连接起来等。
long count()
Optional<T> max(Comparator<? super T> comparator)
Optional<T> reduce(BinaryOperator<T> accumulator)
1
2
3List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b); // or Integer::sum
sum.ifPresent(System.out::println); // 输出: 15
收集:将流元素聚合到容器中
将流中的元素重新组织成另一种形式,通常是一个集合(如List, Set, Map)。语法:<R, A> R collect(Collector<? super T, A, R> collector)
。
1 |
|
Collectors.toList()
Collectors.toMap()
1
2
3
4
5Map<String, Integer> nameToAgeMap = people.stream()
.collect(Collectors.toMap(
p -> p.getName(), // 获取Key
p -> p.getAge() // 获取Value
));Collectors.joining()
1
2
3String names = people.stream()
.map(Person::getName) // Stream<Person> -> Stream<String>
.collect(Collectors.joining(", "));Collectors.summingDouble()
1
2
3Double totalElectronicsPrice = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.collect(Collectors.summingDouble(Product::getPrice));
Optional类
1 |
|
Optional 的诞生正是为了在类型系统层面解决这个问题。
Definition
java.util.Optional<T>
是一个容器对象,它最多可以持有一个非 null 的 T 类型的值。
创建Optional
Optional.of(T value)
,当你确定 value 绝对不为 null 时使用。如果 value 为 null,它会立即抛出 NullPointerException。1
Optional<String> name = Optional.of("John Doe");
Optional.ofNullable(T value)
,当你持有一个可能为 null 的值时使用。如果 value 不为 null,它会创建一个包含该值的 Optional;如果 value 为 null,它会创建一个空的 Optional。1
Optional<User> user = Optional.ofNullable(findUserFromDB());
Optional.ofNullable(T value)
,如果 value 不为 null,它会创建一个包含该值的 Optional;如果 value 为 null,它会创建一个空的 Optional。Optional.empty()
,创建一个空的 Optional 实例。
使用Optional
ifPresent(Consumer<? super T> consumer)
如果你只想在值存在时执行某个操作,可以使用 ifPresent() 方法。
传统方法:1
2
3
4String name = getNullableName();
if (name != null) {
System.out.println("Name is: " + name);
}使用 ifPresen:
1
Optional.ofNullable(getNullableName()).ifPresent(name -> System.out.println("Name is: " + name));
orElse(T other)
andorElseGet(Supplier<? extends T> other)
当 Optional 为空时,我们常常需要提供一个默认值。1
2String name = Optional.ofNullable(getNullableName()).orElse("default");
String name2 = Optional.ofNullable(getNullableName()).orElseGet(() -> createDefaultName());Optional
配合map()
1
2
3
4
5
6Optional<User> userOptional = Optional.ofNullable(getUser());
Optional<String> zipCodeOptional = userOptional
.map(User::getAddress)
.map(Address::getZipCode);
String zipCode = zipCodeOptional.orElse("Not available");以上例子中,
map(User::getAddress)
将Optional<User>
转换为了Optional<Address>
,整个过程无需任何 null 检查。Optional
配合filter()
1
2
3
4
5
6Optional<String> name = Optional.of("John Doe");
// 只有当名字包含 "John" 时才保留
Optional<String> johnOptional = name.filter(n -> n.contains("John")); // 结果为 Optional["John Doe"]
Optional<String> janeOptional = name.filter(n -> n.contains("Jane")); // 结果为 Optional.empty