Java Stream 流處理詳解
Stream 是 Java 8 引入的一個強大的數據處理抽象,它允許你以聲明式方式處理數據集合(類似于 SQL 語句),支持并行操作,提高了代碼的可讀性和處理效率。
一、Stream 的核心概念
1. 什么是 Stream
-
不是數據結構:不存儲數據,只是從數據源(集合、數組等)獲取數據
-
函數式風格:支持 lambda 表達式和方法引用
-
延遲執行:許多操作(中間操作)不會立即執行,只有遇到終止操作才會執行
-
可消費性:Stream 只能被消費一次,用完即失效
2. 操作類型
-
中間操作(Intermediate Operations):返回 Stream 本身,可以鏈式調用(如 filter, map)
-
終止操作(Terminal Operations):產生最終結果或副作用(如 forEach, collect)
二、創建 Stream 的多種方式
// 1. 從集合創建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream(); // 順序流
Stream<String> parallelStream = list.parallelStream(); // 并行流// 2. 從數組創建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);// 3. 使用Stream.of()
Stream<String> stream3 = Stream.of("a", "b", "c");// 4. 使用Stream.generate() 無限流
Stream<Double> randomStream = Stream.generate(Math::random).limit(5);// 5. 使用Stream.iterate() 迭代流
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(10);
三、常用的中間操作
1. 過濾操作
// filter(Predicate) 過濾符合條件的元素
List<String> filtered = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
2. 映射操作
// map(Function) 將元素轉換為其他形式
List<Integer> lengths = list.stream().map(String::length).collect(Collectors.toList());// flatMap 將多個流合并為一個流
List<String> flatMapped = list.stream().flatMap(s -> Stream.of(s.split(""))).collect(Collectors.toList());
3. 去重和排序
// distinct() 去重
List<String> distinct = list.stream().distinct().collect(Collectors.toList());// sorted() 自然排序
List<String> sorted = list.stream().sorted().collect(Collectors.toList());// sorted(Comparator) 自定義排序
List<String> customSorted = list.stream().sorted((s1, s2) -> s2.compareTo(s1)).collect(Collectors.toList());
4. 其他中間操作
// limit(long) 限制元素數量
// skip(long) 跳過前N個元素
// peek(Consumer) 查看流中元素(主要用于調試)
四、常用的終止操作
1. 遍歷操作
// forEach(Consumer) 遍歷每個元素
list.stream().forEach(System.out::println);
2. 收集結果
// collect(Collector) 將流轉換為集合或其他形式
List<String> collectedList = stream.collect(Collectors.toList());
Set<String> collectedSet = stream.collect(Collectors.toSet());
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
3. 聚合操作
// count() 計數
long count = list.stream().count();// max/min(Comparator) 最大/最小值
Optional<String> max = list.stream().max(Comparator.naturalOrder());// reduce 歸約操作
Optional<Integer> sum = Stream.of(1, 2, 3).reduce(Integer::sum);
4. 匹配操作
// anyMatch 任意元素匹配
boolean anyStartsWithA = list.stream().anyMatch(s -> s.startsWith("a"));// allMatch 所有元素匹配
boolean allStartsWithA = list.stream().allMatch(s -> s.startsWith("a"));// noneMatch 沒有元素匹配
boolean noneStartsWithZ = list.stream().noneMatch(s -> s.startsWith("z"));
五、數值流特化
Java 8 提供了專門的數值流,避免裝箱拆箱開銷:
// IntStream, LongStream, DoubleStream
IntStream intStream = IntStream.range(1, 100); // 1-99
DoubleStream doubleStream = DoubleStream.of(1.1, 2.2);// 常用數值操作
int sum = IntStream.rangeClosed(1, 100).sum(); // 1-100的和
OptionalDouble avg = IntStream.of(1, 2, 3).average();
六、并行流處理
// 創建并行流
List<String> parallelProcessed = list.parallelStream().filter(s -> s.length() > 1).collect(Collectors.toList());// 注意事項:
// 1. 確保操作是線程安全的
// 2. 避免有狀態的操作
// 3. 數據量足夠大時才使用并行流
七、收集器(Collectors)的高級用法
// 分組
Map<Integer, List<String>> groupByLength = list.stream().collect(Collectors.groupingBy(String::length));// 分區
Map<Boolean, List<String>> partition = list.stream().collect(Collectors.partitioningBy(s -> s.startsWith("a")));// 連接字符串
String joined = list.stream().collect(Collectors.joining(", "));// 匯總統計
IntSummaryStatistics stats = list.stream().collect(Collectors.summarizingInt(String::length));
八、實際應用示例
示例1:處理對象集合
List<Person> people = ...;// 獲取所有成年人的姓名列表
List<String> adultNames = people.stream().filter(p -> p.getAge() >= 18).map(Person::getName).collect(Collectors.toList());// 按城市分組
Map<String, List<Person>> byCity = people.stream().collect(Collectors.groupingBy(Person::getCity));// 計算每個城市的平均年齡
Map<String, Double> avgAgeByCity = people.stream().collect(Collectors.groupingBy(Person::getCity,Collectors.averagingInt(Person::getAge)));
示例2:文件處理
// 讀取文件并處理
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {long wordCount = lines.flatMap(line -> Arrays.stream(line.split("\\s+"))).filter(word -> word.length() > 0).count();
} catch (IOException e) {e.printStackTrace();
}
九、注意事項
-
流只能消費一次:嘗試第二次使用已關閉的流會拋出?
IllegalStateException
-
避免修改源數據:流操作期間不應修改源集合
-
合理使用并行流:并非所有情況都適合并行,小數據量可能適得其反
-
注意自動裝箱:數值操作盡量使用原始類型特化流(IntStream等)
-
延遲執行特性:沒有終止操作,中間操作不會執行
Stream API 提供了一種高效、聲明式的數據處理方式,是現代 Java 編程中不可或缺的工具。合理使用可以大幅提升代碼的可讀性和維護性。