简介
Java 中的“流”主要指的是 Java Stream API(自 Java 8 引入),它为处理集合数据提供了一种高效、可读、函数式的方式
什么是 Stream
- 是对集合对象功能的增强
- 不是数据结构,不存储数据,只是数据的传输渠道(数据流水线)
- 操作是惰性执行的,只有在需要结果时才执行(如
collect()
)
三大操作步骤
Java 流操作可分为三种类型,创建流、中间操作、终结操作
创建流
简单的示例
import java.util.stream.*;
public class StreamOf {
public static void main(String[] args) {
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!").forEach(System.out::print);
System.out.println();
Stream.of(3.14159, 2.718, 1.618).forEach(System.out::println);
}
}
使用Stream.of
可以轻松将一组条目变成一个流
import java.util.*;
public class CollectionToStream {
public static void main(String[] args) {
Set<String> w = new HashSet<>(Arrays.asList(
"It's a wonderful day for pie!".split(" ")
));
w.stream().map(x -> x + " ")
.forEach(System.out::print);
System.out.println();
Map<String, Double> m = new HashMap<>();
m.put("pi", 3.14159);
m.put("e", 2.718);
m.put("phi", 1.618);
m.entrySet().stream().map(e -> e.getKey() + ":" + e.getValue()).forEach(System.out::println);
}
}
为了从 Map 集合生成一个流,我们首先调用 entrySet 方法来生成一个对象流,List 和 Set 类使用 stream 方法就可以了
随机数流
Random random = new Random();
// 生成 IntStream(整数流)
IntStream intStream = random.ints(); // 无限整数流
IntStream intStreamLimited = random.ints(10); // 限定长度
IntStream intBounded = random.ints(10, 1, 100); // 10个 [1, 100) 的随机整数
// 生成 LongStream(长整型流)
LongStream longStream = random.longs(5, 10, 100);
// 生成 DoubleStream(浮点型流)
DoubleStream doubleStream = random.doubles(5, 0.0, 1.0);
文件流
使用 Files.lines 方法可以创建文件流
Path path = Paths.get("test.txt");
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
}
无限流
Stream.generate()
是 Java 8 引入的 Stream API 中用来 生成无限流(infinite stream) 的方法之一,非常适合构造随机数、常量流、自定义对象等。但是必须搭配 .limit(n)
使用,否则会无限执行下去。
import java.util.stream.Stream;
public class GenerateStream {
public static void main(String[] args) {
Stream<String> helloStream = Stream.generate(() -> "hello");
helloStream.limit(10).forEach(System.out::println);
// 输出 10 次 hello
}
}
iterate
public static void main(String[] args) {
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
}
第一个参数是种子 n = 0,然后每次用
n -> n + 2
计算下一个。
流生成器
Stream.builder()
是 Java 8 中 Stream
提供的一种用于动态构建流的方法,适合你事先不知道流中有多少个元素,但想一个一个手动添加的场景
import java.util.stream.Stream;
public class StreamBuilderExample {
public static void main(String[] args) {
Stream.Builder<String> builder = Stream.builder();
builder.add("Java");
builder.add("Python");
builder.add("Go");
Stream<String> stream = builder.build();
stream.forEach(System.out::println);
}
}
中间操作
Java 中 Stream 的中间操作(Intermediate Operations) 是指:在 Stream 处理流程中,返回一个新的 Stream 的操作,这些操作不会立即执行,只有当终端操作(Terminal Operation)调用时,才会触发实际的处理
操作 | 含义 | 示例 |
---|---|---|
filter(Predicate) |
过滤元素 | stream.filter(x -> x > 5) |
map(Function) |
元素映射(转换) | stream.map(String::toUpperCase) |
flatMap(Function) |
扁平化嵌套结构 | stream.flatMap(x -> Arrays.stream(x)) |
distinct() |
去重 | stream.distinct() |
sorted() / sorted(Comparator) |
排序 | stream.sorted() or stream.sorted(Comparator.reverseOrder()) |
peek(Consumer) |
查看每个元素(调试用) | stream.peek(System.out::println) |
limit(n) |
截取前 n 个元素 | stream.limit(10) |
skip(n) |
跳过前 n 个元素 | stream.skip(5) |
示例
import java.util.stream.Stream;
public class IntermediateExample {
public static void main(String[] args) {
Stream.of("apple", "apple", "banana", "apricot", "cherry")
.filter(s -> s.startsWith("a")) // 只保留以 a 开头的
.map(String::toUpperCase) // 转成大写
.sorted() // 排序
// .peek(System.out::println) // 中间观察
.distinct() // 过滤
.limit(10) // 最多两个
.forEach(System.out::println); // 终止操作
}
}
Optional 类型
Optional 类型解决了流中没有数据,产生空指针异常的问题。
常用方法
操作 | 返回类型 | 含义 |
---|---|---|
findFirst() |
Optional<T> |
获取第一个元素,可能为空 |
findAny() |
Optional<T> |
获取任意元素,适用于并行流 |
max() |
Optional<T> |
获取最大元素,可能集合为空 |
min() |
Optional<T> |
获取最小元素 |
reduce() |
Optional<T> |
合并操作,若无初始值则结果可能为空 |
判断是否空流
import java.util.Optional;
import java.util.stream.Stream;
public class OptionalBasics {
static void test(Optional<String> optString) {
if (optString.isPresent())
System.out.println(optString.get());
else
System.out.println("Nothing inside!");
}
public static void main(String[] args) {
test(Stream.of("Epithets").findFirst());
test(Stream.<String> empty().findFirst());
}
}
便捷函数
在 Java 的 Optional
中,有一组常用的便捷函数(Convenience Methods),用于优雅、安全地处理可能为空的值。它们是函数式编程风格的体现,广泛用于 Stream、数据库结果处理等场景
构造函数
方法 | 含义 |
---|---|
Optional.of(value) |
创建非空 Optional,值不能为 null,否则抛出异常 |
Optional.ofNullable(value) |
创建 Optional,值可以为 null |
Optional.empty() |
创建空的 Optional 实例 |
Optional<String> opt1 = Optional.of("hello");
Optional<String> opt2 = Optional.ofNullable(null);
Optional<String> opt3 = Optional.empty();
判断与获取值
方法 | 说明 |
---|---|
isPresent() |
判断是否有值 |
isEmpty() (Java 11+) |
判断是否为空 |
get() |
获取值(不推荐直接使用,值为空时会抛异常) |
orElse(T) |
如果为空返回默认值 |
orElseGet(Supplier) |
如果为空返回 Lambda 提供的值(延迟执行) |
orElseThrow() |
若为空则抛 NoSuchElementException |
orElseThrow(Supplier) |
若为空抛出自定义异常 |
public class OptionalExamples {
public static void main(String[] args) {
// 1. isPresent():判断是否有值
Optional<String> opt1 = Optional.of("hello");
if (opt1.isPresent()) {
System.out.println("有值: " + opt1.get()); // 输出:有值: hello
}
// 2. isEmpty()(Java 11+)
Optional<String> opt2 = Optional.empty();
if (opt2.isEmpty()) {
System.out.println("值是空的"); // 输出:值是空的
}
// 3. get()(不推荐直接使用,除非你确定值一定存在)
Optional<String> opt3 = Optional.of("world");
String value3 = opt3.get(); // 安全获取
System.out.println("使用 get(): " + value3); // 输出:world
// 4. orElse(T):为空返回默认值
Object name4 = Optional.ofNullable(null).orElse("默认值");
System.out.println("orElse 返回: " + name4); // 输出:默认值
// 5. orElseGet(Supplier):为空返回 Lambda 提供的值(延迟执行)
Object name5 = Optional.ofNullable(null)
.orElseGet(() -> {
System.out.println("调用了 orElseGet");
return "延迟默认值";
});
System.out.println("orElseGet 返回: " + name5); // 输出:延迟默认值
// 6. orElseThrow():为空抛出 NoSuchElementException
Optional<String> opt6 = Optional.of("存在值");
String value6 = opt6.orElseThrow(); // 正常返回
System.out.println("orElseThrow 返回: " + value6);
Optional<String> emptyOpt = Optional.empty();
// String error6 = emptyOpt.orElseThrow(); // 抛 NoSuchElementException
// System.out.println(error6);
// 7. orElseThrow(Supplier):为空时抛出自定义异常
Optional<String> opt7 = Optional.ofNullable(null);
try {
String value7 = opt7.orElseThrow(() -> new IllegalArgumentException("值不存在"));
System.out.println("value7 = " + value7);
} catch (IllegalArgumentException e) {
System.out.println("捕获异常: " + e.getMessage()); // 输出:值不存在
}
}
}
变换与过滤
方法 | 说明 |
---|---|
map(Function) |
映射值,如果存在就应用函数 |
flatMap(Function) |
和 map 类似,但函数返回 Optional(避免嵌套) |
filter(Predicate) |
如果值存在且满足条件则保留,否则变成 empty |
public class OptionalDemo {
public static void main(String[] args) {
Optional<String> name = Optional.of("bear");
// 1. map:将字符串映射为大写
Optional<String> upperName = name.map(String::toUpperCase);
System.out.println("map 结果: " + upperName.orElse("空")); // 输出:BEAR
// 2. flatMap:获取字符串长度(返回 Optional)
Optional<Integer> nameLength = name.flatMap(n -> Optional.of(n.length()));
System.out.println("flatMap 结果: " + nameLength.orElse(-1)); // 输出:4
// 3. filter:只保留长度 > 3 的字符串
Optional<String> longName = name.filter(n -> n.length() > 3);
System.out.println("filter(>3) 结果: " + longName.orElse("不满足条件")); // 输出:bear
// 4. filter:只保留长度 < 3 的字符串
Optional<String> shortName = name.filter(n -> n.length() < 3);
System.out.println("filter(<3) 结果: " + shortName.orElse("不满足条件")); // 输出:不满足条件
}
}
终结操作
Java 流(Stream)中的终结操作(Terminal Operation) 是触发流中处理逻辑的最后一步,它将流的中间操作“收尾”,并返回一个最终结果或副作用(比如打印、求和等)。执行终结操作后,流就不能再使用了
常见
方法名 | 说明 |
---|---|
forEach(Consumer) |
遍历每个元素(有副作用) |
toArray() |
转为数组 |
reduce(...) |
归约操作(如求和、拼接等) |
collect(...) |
收集结果(转集合、字符串等) |
count() |
统计数量 |
min(Comparator) |
最小值 |
max(Comparator) |
最大值 |
anyMatch(Predicate) |
任意匹配 |
allMatch(Predicate) |
全部匹配 |
noneMatch(Predicate) |
全不匹配 |
findFirst() |
找第一个元素 |
findAny() |
找任意一个(并行流更有效) |
import java.util.*;
import java.util.stream.*;
public class TerminalOperationsDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 1. forEach
names.stream().forEach(System.out::println);
// 2. toArray
Object[] array = names.stream().toArray();
System.out.println("数组长度: " + array.length);
// 3. reduce
String joined = names.stream().reduce((a, b) -> a + ", " + b).orElse("无内容");
System.out.println("连接结果: " + joined);
// 4. collect
List<String> filtered = names.stream()
.filter(n -> n.length() > 3)
.collect(Collectors.toList());
System.out.println("过滤后: " + filtered);
// 5. count
long count = names.stream().filter(n -> n.contains("a")).count();
System.out.println("包含字母 a 的数量: " + count);
// 6. min & max
String minName = names.stream().min(Comparator.comparing(String::length)).orElse("无");
System.out.println("最短名字: " + minName);
// 7. anyMatch, allMatch, noneMatch
boolean any = names.stream().anyMatch(n -> n.startsWith("A"));
boolean all = names.stream().allMatch(n -> n.length() >= 3);
boolean none = names.stream().noneMatch(n -> n.equals("Zoe"));
System.out.println("任意以 A 开头: " + any);
System.out.println("全部长度 >= 3: " + all);
System.out.println("没有人叫 Zoe: " + none);
// 8. findFirst & findAny
Optional<String> first = names.stream().findFirst();
first.ifPresent(n -> System.out.println("第一个: " + n));
}
}