一、Stream 基本概念
1.1 Stream 是什么
Java Stream 是 Java 8 引入的处理集合数据的 API,支持声明式编程和函数式操作。
java
// 传统方式 vs Stream 方式
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 传统方式
List<String> result = new ArrayList<>();
for (String name : names) {
if (name.startsWith("A")) {
result.add(name.toUpperCase());
}
}
// Stream 方式
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
二、Stream 创建方式
2.1 从集合创建
java
// 从 List 创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
Stream<String> parallelStream = list.parallelStream();
// 从 Set 创建
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
Stream<Integer> stream2 = set.stream();
2.2 从数组创建
java
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
// 指定范围
Stream<String> rangeStream = Arrays.stream(array, 1, 3); // "b", "c"
2.3 使用 Stream.of()
java
Stream<String> stream = Stream.of("a", "b", "c");
Stream<Integer> intStream = Stream.of(1, 2, 3);
2.4 使用 Builder
java
Stream<String> stream = Stream.<String>builder()
.add("a")
.add("b")
.add("c")
.build();
2.5 生成无限流
java
// 生成无限序列
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2)
.limit(10); // 限制为前10个
// 生成随机数
Stream<Double> randomStream = Stream.generate(Math::random)
.limit(5);
2.6 基本类型 Stream
java
// IntStream
IntStream intStream = IntStream.range(1, 10); // 1-9
IntStream closedIntStream = IntStream.rangeClosed(1, 10); // 1-10
// LongStream
LongStream longStream = LongStream.range(1, 100);
// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);
三、中间操作(Intermediate Operations)
3.1 filter() - 过滤
java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 过滤长度大于3的名字
List<String> filtered = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList()); // [Alice, Charlie, David]
// 多条件过滤
List<String> multiFiltered = names.stream()
.filter(name -> name.length() > 3)
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList()); // [Alice]
3.2 map() - 映射转换
java
// 转换大小写
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 提取对象属性
class Person {
private String name;
private int age;
// getters, setters
}
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30)
);
List<String> personNames = persons.stream()
.map(Person::getName)
.collect(Collectors.toList());
// 复杂转换
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
3.3 flatMap() - 扁平化映射
java
// 将多个集合合并为一个流
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()); // [a, b, c, d]
// 处理嵌套对象
class Order {
private List<Item> items;
// getter, setter
}
class Item {
private String name;
private double price;
// getter, setter
}
List<Order> orders = Arrays.asList(order1, order2);
List<Item> allItems = orders.stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.toList());
3.4 distinct() - 去重
java
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList()); // [1, 2, 3, 4]
3.5 sorted() - 排序
java
// 自然排序
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// 自定义排序
List<Person> sortedByAge = persons.stream()
.sorted(Comparator.comparingInt(Person::getAge))
.collect(Collectors.toList());
// 多重排序
List<Person> multiSorted = persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge))
.collect(Collectors.toList());
// 反向排序
List<Person> reverseSorted = persons.stream()
.sorted(Comparator.comparing(Person::getAge).reversed())
.collect(Collectors.toList());
3.6 limit() 和 skip() - 限制和跳过
java
List<Integer> numbers = IntStream.rangeClosed(1, 10)
.boxed()
.collect(Collectors.toList());
// 获取前5个
List<Integer> firstFive = numbers.stream()
.limit(5)
.collect(Collectors.toList()); // [1, 2, 3, 4, 5]
// 跳过前3个
List<Integer> skipThree = numbers.stream()
.skip(3)
.collect(Collectors.toList()); // [4, 5, 6, 7, 8, 9, 10]
// 分页实现
int pageSize = 3;
int pageNum = 2; // 第二页
List<Integer> page = numbers.stream()
.skip((pageNum - 1) * pageSize)
.limit(pageSize)
.collect(Collectors.toList()); // [4, 5, 6]
3.7 peek() - 调试查看
java
List<String> result = names.stream()
.peek(System.out::println) // 调试输出
.filter(name -> name.length() > 3)
.peek(name -> System.out.println("Filtered: " + name))
.collect(Collectors.toList());
四、终端操作(Terminal Operations)
4.1 forEach() - 遍历
java
// 遍历输出
names.stream().forEach(System.out::println);
// 并行流遍历(无序)
names.parallelStream().forEach(System.out::println);
// 并行流有序遍历
names.parallelStream().forEachOrdered(System.out::println);
4.2 collect() - 收集
java
// 收集到List
List<String> list = names.stream()
.filter(n -> n.length() > 3)
.collect(Collectors.toList());
// 收集到Set(自动去重)
Set<String> set = names.stream()
.collect(Collectors.toSet());
// 收集到特定集合
TreeSet<String> treeSet = names.stream()
.collect(Collectors.toCollection(TreeSet::new));
// 收集到Map
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(
Function.identity(), // key: 名字本身
String::length // value: 名字长度
));
// 处理key冲突
Map<String, Integer> mapWithConflict = names.stream()
.collect(Collectors.toMap(
Function.identity(),
String::length,
(oldValue, newValue) -> oldValue // 冲突时保留旧值
));
4.3 toArray() - 转换为数组
java
String[] array = names.stream().toArray(String[]::new);
// 基本类型数组
int[] intArray = IntStream.range(1, 5).toArray();
4.4 reduce() - 归约
java
// 求和
Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5)
.reduce(Integer::sum);
// 或
int sumResult = Stream.of(1, 2, 3, 4, 5)
.reduce(0, Integer::sum);
// 求最大值
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5)
.reduce(Integer::max);
// 字符串连接
String concatenated = Stream.of("a", "b", "c")
.reduce("", String::concat);
// 复杂对象归约
class Order {
private List<Item> items;
// getter, setter
}
double totalPrice = orders.stream()
.flatMap(order -> order.getItems().stream())
.map(Item::getPrice)
.reduce(0.0, Double::sum);
4.5 匹配操作
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 任意匹配
boolean anyMatch = numbers.stream()
.anyMatch(n -> n > 3); // true
// 全部匹配
boolean allMatch = numbers.stream()
.allMatch(n -> n > 0); // true
// 无匹配
boolean noneMatch = numbers.stream()
.noneMatch(n -> n < 0); // true
4.6 查找操作
java
Optional<String> first = names.stream()
.filter(n -> n.startsWith("A"))
.findFirst(); // 返回第一个元素
Optional<String> any = names.stream()
.filter(n -> n.startsWith("A"))
.findAny(); // 并行流中效率更高
4.7 统计操作
java
// 计数
long count = names.stream().count();
// 使用 IntStream 统计
IntSummaryStatistics stats = IntStream.range(1, 100)
.summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());
五、Collectors 高级用法
5.1 分组
java
List<Person> persons = Arrays.asList(
new Person("Alice", 25, "New York"),
new Person("Bob", 30, "New York"),
new Person("Charlie", 25, "London")
);
// 按年龄分组
Map<Integer, List<Person>> groupByAge = persons.stream()
.collect(Collectors.groupingBy(Person::getAge));
// 多级分组
Map<Integer, Map<String, List<Person>>> multiGroup = persons.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.groupingBy(Person::getCity)
));
// 分组并计数
Map<Integer, Long> ageCount = persons.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.counting()
));
// 分组并求平均值
Map<Integer, Double> averageAgeByCity = persons.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.averagingInt(Person::getAge)
));
5.2 分区
java
// 按条件分区
Map<Boolean, List<Person>> partition = persons.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 25));
// 分区并统计
Map<Boolean, Long> partitionCount = persons.stream()
.collect(Collectors.partitioningBy(
p -> p.getAge() > 25,
Collectors.counting()
));
5.3 连接字符串
java
// 简单连接
String joined = names.stream()
.collect(Collectors.joining()); // "AliceBobCharlie"
// 带分隔符
String joinedWithComma = names.stream()
.collect(Collectors.joining(", ")); // "Alice, Bob, Charlie"
// 带前缀和后缀
String joinedFull = names.stream()
.collect(Collectors.joining(", ", "[", "]"));
// "[Alice, Bob, Charlie]"
5.4 统计汇总
java
// 求总和
int totalAge = persons.stream()
.collect(Collectors.summingInt(Person::getAge));
// 求平均值
double averageAge = persons.stream()
.collect(Collectors.averagingInt(Person::getAge));
// 综合统计
IntSummaryStatistics ageStats = persons.stream()
.collect(Collectors.summarizingInt(Person::getAge));
六、并行流(Parallel Stream)
6.1 创建并行流
java
// 从集合创建并行流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> parallelStream = names.parallelStream();
// 将顺序流转为并行流
Stream<String> parallel = names.stream().parallel();
// 将并行流转为顺序流
Stream<String> sequential = parallelStream.sequential();
6.2 并行流注意事项
java
// 非线程安全操作(错误示例)
List<Integer> unsafeList = new ArrayList<>();
IntStream.range(0, 10000).parallel()
.forEach(unsafeList::add); // 可能丢失数据
// 线程安全操作(正确示例)
List<Integer> safeList = IntStream.range(0, 10000).parallel()
.boxed()
.collect(Collectors.toList());
// 并行流排序
List<Integer> sortedParallel = IntStream.range(0, 10000).parallel()
.sorted()
.boxed()
.collect(Collectors.toList());
6.3 并行流性能考虑
java
// 小数据量顺序流更快
List<Integer> smallList = IntStream.range(0, 100)
.boxed().collect(Collectors.toList());
long start = System.currentTimeMillis();
smallList.stream().map(i -> i * 2).count();
long sequentialTime = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
smallList.parallelStream().map(i -> i * 2).count();
long parallelTime = System.currentTimeMillis() - start;
System.out.println("Sequential: " + sequentialTime + "ms");
System.out.println("Parallel: " + parallelTime + "ms");
七、实战案例
7.1 数据处理管道
java
public class StreamPipelineExample {
public static void main(String[] args) {
List<Transaction> transactions = Arrays.asList(
new Transaction("USD", 1000.0, Transaction.Type.DEPOSIT),
new Transaction("USD", 500.0, Transaction.Type.WITHDRAWAL),
new Transaction("EUR", 800.0, Transaction.Type.DEPOSIT),
new Transaction("USD", 300.0, Transaction.Type.DEPOSIT),
new Transaction("JPY", 10000.0, Transaction.Type.WITHDRAWAL)
);
// 复杂数据处理管道
Map<String, Double> totalDepositsByCurrency = transactions.stream()
.filter(t -> t.getType() == Transaction.Type.DEPOSIT)
.filter(t -> t.getAmount() > 200)
.collect(Collectors.groupingBy(
Transaction::getCurrency,
Collectors.summingDouble(Transaction::getAmount)
));
totalDepositsByCurrency.forEach((currency, total) ->
System.out.println(currency + ": " + total));
}
}
7.2 文件处理
java
public class FileStreamExample {
public static void main(String[] args) throws IOException {
// 读取文件并处理
List<String> lines = Files.lines(Paths.get("data.txt"))
.filter(line -> !line.trim().isEmpty())
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// 写入文件
Files.write(Paths.get("output.txt"), lines);
}
}
7.3 数据库查询模拟
java
public class DatabaseStreamExample {
public static List<User> findActiveUsers(List<User> users) {
return users.stream()
.filter(User::isActive)
.filter(user -> user.getLastLogin().isAfter(LocalDate.now().minusDays(30)))
.sorted(Comparator.comparing(User::getLastLogin).reversed())
.collect(Collectors.toList());
}
}
八、性能优化建议
8.1 使用基本类型流
java
// 避免装箱拆箱开销
// 不好
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue) // 需要拆箱
.sum();
// 好
int sum = IntStream.rangeClosed(1, 5).sum();
8.2 短路操作优化
java
// 使用短路操作提前结束
boolean hasMatch = largeList.stream()
.filter(this::expensiveOperation)
.anyMatch(this::condition); // 找到第一个匹配就停止
8.3 避免重复计算
java
// 不好 - 重复计算
List<String> result = names.stream()
.filter(name -> expensiveCheck(name))
.map(name -> transform(name))
.filter(transformed -> expensiveCheck(transformed))
.collect(Collectors.toList());
// 好 - 缓存中间结果
List<String> filtered = names.stream()
.filter(name -> expensiveCheck(name))
.collect(Collectors.toList());
List<String> result = filtered.stream()
.map(name -> transform(name))
.filter(transformed -> expensiveCheck(transformed))
.collect(Collectors.toList());
九、常见陷阱
9.1 Stream 只能消费一次
java
Stream<String> stream = names.stream();
stream.forEach(System.out::println);
// stream.count(); // 错误!Stream 已经关闭
9.2 空指针处理
java
// 使用 Optional 避免空指针
Optional<String> first = names.stream()
.filter(Objects::nonNull)
.findFirst();
// 或者在创建 Stream 时处理
Stream<String> safeStream = Optional.ofNullable(names)
.orElse(Collections.emptyList())
.stream();
9.3 修改外部状态
java
// 避免副作用
List<String> result = new ArrayList<>();
names.stream()
.forEach(result::add); // 不好
// 使用 collect
List<String> goodResult = names.stream()
.collect(Collectors.toList()); // 好
总结
Java Stream 提供了强大、灵活的数据处理能力,核心要点:
-
创建流:多种创建方式,灵活选择
-
中间操作:链式调用,延迟执行
-
终端操作:触发计算,产生结果
-
并行流:合理使用提升性能
-
Collectors:强大的结果收集工具
在实际开发中,应根据具体场景选择合适的流操作,注意性能优化,避免常见陷阱。