Stream API
Stream API概述
什么是 Stream API
- Java 8 引入:从 JDK 1.8 开始,Java 推出了
Stream API,将函数式编程思想引入 Java。- 声明式操作集合:Stream API 支持声明式操作,可以对集合进行过滤、映射、排序等处理,无需手动编写循环。
- 不修改原始数据源:Stream 操作返回新的数据流,原始集合保持不变,保证数据安全。
- 顺序与并行处理:Stream 可顺序执行,也可使用并行流利用多核 CPU 并行处理,提高大数据处理效率。
总结:Stream API 提供高效、灵活、可组合的数据处理方式,使 Java 代码更简洁、易读、易维护。
import java.util.List; import java.util.Arrays; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> result = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList()); System.out.println(result); // 输出: [Alice] } }
Stream 和 Collection
- Collection:强调数据存储,是静态的内存数据结构,比如
List、Set、Map。- Stream:强调计算操作,是对集合进行处理的“数据流”,不存储数据,只做中间计算。
总结:Collection 面向内存,用来存储和管理数据;Stream 面向 CPU,用来处理数据、生成结果。
Stream 的核心特点
- 不存储数据:Stream 只负责计算数据流,不保存元素本身。
- 不修改数据源:操作返回新的 Stream,原始集合保持不变。
- 延迟执行:中间操作如
filter、map是惰性执行的,只有终止操作(如collect、forEach)才触发计算。- 一次性使用:Stream 只能使用一次,使用过后必须重新创建,否则会抛出
IllegalStateException。- 顺序流与并行流:顺序流
stream(),单线程顺序处理。并行流parallelStream(),自动拆分数据,多线程处理。注意:并行流可能改变元素处理顺序,避免在并行流中使用共享可变状态。import java.util.stream.Stream; public class Main { public static void main(String[] args) { // 创建流,中间操作 map 并未执行 Stream<String> stream1 = Stream.of("A", "B", "C") .map(s -> { System.out.println("map: " + s); return s.toLowerCase(); }); System.out.println("========="); // 第一次终止操作,触发流计算 stream1.forEach(System.out::println); System.out.println("========="); // 流已经被消费完,重新创建流才能再次使用 Stream<String> stream2 = Stream.of("A", "B", "C") .map(s -> { System.out.println("map: " + s); return s.toLowerCase(); }); // 第二次终止操作 stream2.forEach(System.out::println); } }
Stream 的操作流程
操作流程:数据源 → 中间操作 → 中间操作 → … → 终止操作 → 结果
创建 Stream:从集合、数组、文件、生成器等生成 Stream。
List<String> list = Arrays.asList("Java", "Python", "C++"); Stream<String> stream = list.stream();中间操作(Intermediate):返回新的 Stream,可链式调用,实现过滤、映射、排序等操作。
Stream<String> filtered = stream .filter(s -> s.length() > 3) .map(String::toUpperCase);终止操作(Terminal):触发计算并返回最终结果,例如:
collect、forEach、count。List<String> result = filtered .collect(Collectors.toList()); result.forEach(System.out::println);
创建 Stream 的方式
从 Collection 创建
最常见的方式,几乎所有集合类都支持,生成的 Stream 可以顺序或并行处理集合元素。
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { List<String> list = Arrays.asList("Java", "Python", "C++"); // 顺序流 Stream<String> sequentialStream = list.stream(); System.out.println("顺序流输出:"); sequentialStream.forEach(System.out::println); // 并行流 Stream<String> parallelStream = list.parallelStream(); System.out.println("并行流输出:"); parallelStream.forEach(System.out::println); } }
从数组创建
适合对数组元素进行流式操作,既可以用
Arrays.stream(),也可以用Stream.of()。import java.util.stream.Stream; import java.util.Arrays; public class Main { public static void main(String[] args) { String[] arr = {"A", "B", "C"}; // 使用 Arrays.stream() Stream<String> stream1 = Arrays.stream(arr); System.out.println("使用 Arrays.stream():"); stream1.forEach(System.out::println); // 使用 Stream.of() Stream<String> stream2 = Stream.of(arr); System.out.println("使用 Stream.of():"); stream2.forEach(System.out::println); } }
使用 Stream 静态方法
Stream.of():可创建任意元素的 Stream。Stream.empty(): 可创建空流。Stream.iterate():生成可迭代的无限流。Stream.generate():生成随机或固定逻辑的无限流。import java.util.stream.Stream; public class Main { public static void main(String[] args) { // 创建包含指定元素的流 Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); System.out.println("Stream.of元素流:"); stream.forEach(System.out::println); System.out.println("========="); // 创建空流 Stream<String> emptyStream = Stream.empty(); System.out.println("空流:"); emptyStream.forEach(System.out::println); // 不输出任何内容 System.out.println("========="); // 无限流:generate Stream<Double> randomNumbers = Stream .generate(Math::random) .limit(5); System.out.println("无限流 generate (前5个随机数):"); randomNumbers.forEach(System.out::println); System.out.println("========="); // 无限流:iterate Stream<Integer> naturalNumbers = Stream .iterate(1, n -> n + 1) .limit(10); System.out.println("无限流 iterate (前10个自然数):"); naturalNumbers.forEach(System.out::println); } }
从文件或字符串创建
适合读取外部数据源,生成每行或每个元素的 Stream。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.Arrays; import java.util.regex.Pattern; import java.util.stream.Stream; public class Main { public static void main(String[] args) { // ------------------------------- // 1. 从字符串创建 Stream(方式1:split + Arrays.stream) // ------------------------------- String str = "apple,banana,orange"; Stream<String> strStream1 = Arrays.stream(str.split(",")); System.out.println("从字符串创建 Stream(split + Arrays.stream):"); strStream1.forEach(System.out::println); System.out.println("========="); // ------------------------------- // 2. 从字符串创建 Stream(方式2:Pattern.splitAsStream) // ------------------------------- Stream<String> strStream2 = Pattern.compile(",").splitAsStream(str); System.out.println("从字符串创建 Stream(Pattern.splitAsStream):"); strStream2.forEach(System.out::println); System.out.println("========="); // ------------------------------- // 3. 从文件或网络读取创建 Stream // ------------------------------- String urlStr = "https://raw.githubusercontent.com/github/gitignore/main/Java.gitignore"; // 示例小文本 try { URL url = new URL(urlStr); // BufferedReader.lines() 返回 Stream<String> try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) { Stream<String> lines = reader.lines(); System.out.println("从网络读取文本创建 Stream:"); lines.forEach(System.out::println); } } catch (IOException e) { System.out.println("读取网络文本失败: " + e.getMessage()); } } }
空流或 null 安全流
在集合或对象可能为
null时,使用空流或Optional可避免空指针异常,安全创建 Stream。Optional.ofNullable(obj).stream()可以将可能为null的对象安全地转换为 Stream,方便后续链式操作。import java.util.stream.Stream; import java.util.Optional; public class Main { public static void main(String[] args) { // 创建空流 Stream<Object> emptyStream = Stream.empty(); System.out.println("空流内容:"); emptyStream.forEach(System.out::println); System.out.println("========="); // 使用 Optional 创建 null 安全流 String value = null; // 可以改成非 null 测试 Stream<String> safeStream = Optional.ofNullable(value).stream(); System.out.println("Optional 创建的 null 安全流内容:"); safeStream.forEach(System.out::println); } }
顺序流与并行流
- 顺序流:单线程顺序处理元素,简单直观,适合小数据集。
- 并行流:将数据拆分多线程处理,提升大数据处理性能,但需注意线程安全。
- 并行流并不总比顺序流快,适合 CPU 密集型操作和大数据量场景。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 顺序流 System.out.println("顺序流输出:"); numbers.stream().forEach(System.out::println); System.out.println("========="); // 并行流 System.out.println("并行流输出:"); numbers.parallelStream().forEach(System.out::println); } }
Stream 中间操作
中间操作是惰性执行的,返回新的 Stream,可链式调用。
操作 JDK 作用与说明 使用场景 / 注意事项 filter 8 按条件筛选元素,返回满足条件的 Stream 条件筛选,避免循环手动判断 map 8 一对一映射,每个元素映射为一个新元素 类型转换、字段提取、计算 flatMap 8 一对多映射,扁平化 Stream 展开嵌套集合(List → List)
distinct 8 去重,返回不重复元素 使用 equals() 判断重复,注意性能 sorted 8 排序,默认自然顺序或自定义 Comparator 默认自然顺序,需要实现 Comparable limit 8 截取前 N 个元素 分页或取前几条数据 skip 8 跳过前 N 个元素 分页或跳过不需要的元素 peek 8 遍历元素并执行动作,通常用于调试 不改变元素内容,用于打印、日志 boxed 8 将基本类型流转为对象流 IntStream → Stream 等 asIntStream /
asLongStream /
asDoubleStream8 将对象流转为基本类型流 减少装箱开销,数值计算 takeWhile 9 顺序流中,从头取元素直到条件不满足停止 顺序流明确,遇第一个不满足停止 dropWhile 9 顺序流中,从头丢弃满足条件元素 顺序流明确,遇第一个不满足保留后续 filterNot / filterNotNull 9 / 10 (扩展库) filterNot条件取反,filterNotNull过滤 null官方 Java 无直接实现,多见于 Kotlin / Vavr / Guava mapMulti 21 高性能一对多映射,替代 flatMap 避免中间 Stream 创建,大数据扁平化 takeUntil / dropUntil 21 类似 takeWhile / dropWhile,但条件满足/不满足停止 顺序流条件灵活控制
筛选 filter
根据条件筛选元素,返回满足条件的 Stream。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> list = Arrays.asList("a", "ab", "abc"); list.stream() .filter(s -> s.length() == 2) // 筛选长度为 2 的元素 .forEach(System.out::println); // 输出: ab } }
映射 map
一对一转换,每个输入元素对应一个输出元素,返回新的 Stream。
import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> list = Arrays.asList("a", "ab", "abc"); list.stream() .map(String::toUpperCase) // 将元素转换为大写 .forEach(System.out::println); // 输出: A AB ABC } }
扁平化 flatMap
一对多映射,将每个元素映射为 Stream 并合并成一个 Stream。flatMap可以把多层嵌套结构展开,避免出现
Stream<Stream<T>>。import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4)) .flatMap(List::stream) // 扁平化子列表 .forEach(System.out::println); // 输出: 1 2 3 4 } }
除重 distinct
使用
equals()方法判断元素是否重复,然后去重,返回不重复的元素流。会产生额外开销,数据量大时注意性能。import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of(1, 2, 2, 3) .distinct() // 去重 .forEach(System.out::println); // 输出: 1 2 3 } }
排序 sorted
对 Stream 元素进行排序,可使用自然顺序或自定义比较器。默认按元素的自然顺序排序,需要实现
Comparable。自定义排序可传入Comparator。import java.util.Comparator; import java.util.stream.Stream; public class Main { public static void main(String[] args) { System.out.println("自然顺序排序:"); Stream.of(3, 1, 2) .sorted() // 自然顺序 .forEach(System.out::println); // 输出: 1 2 3 System.out.println("========="); System.out.println("反序排序:"); Stream.of(3, 1, 2) .sorted(Comparator.reverseOrder()) // 反序 .forEach(System.out::println); // 输出: 3 2 1 } }
截断 limit / 跳过 skip
limit(n):截取前 n 个元素。skip(n):跳过前 n 个元素。import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of(1, 2, 3, 4, 5) .skip(2) // 跳过前两个元素 .limit(2) // 取接下来的两个元素 .forEach(System.out::println); // 输出: 3 4 } }
peek
对 Stream 中的每个元素执行动作,不建议用于修改元素内容,主要用于调试和观察中间状态。
import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of("apple", "banana", "avocado") .filter(s -> s.startsWith("a")) .peek(s -> System.out.println("中间元素: " + s)) // 查看中间元素 .map(String::toUpperCase) .forEach(s -> System.out.println("最终输出: " + s)); } }
基本类型流转换
基本类型流转换 boxed / asIntStream / asLongStream / asDoubleStream,将基本类型流和对象流互相转换,减少装箱开销或方便链式操作。
import java.util.stream.IntStream; import java.util.stream.Stream; public class Main { public static void main(String[] args) { IntStream.range(1, 5) .boxed() // IntStream -> Stream<Integer> .forEach(System.out::println); // 输出: 1 2 3 4 System.out.println("========="); Stream.of("1", "2", "3") .mapToInt(Integer::parseInt) // Stream<String> -> IntStream .forEach(System.out::println); // 输出: 1 2 3 } }
条件取元素 takeWhile
从 Stream 开头取元素,直到第一个不满足条件的元素为止。只作用于顺序流(Sequential Stream)时行为明确。
import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of(1, 2, 3, 4, 1, 2) .takeWhile(n -> n < 4) // 条件满足时取元素 .forEach(System.out::println); // 输出: 1 2 3 } }
条件丢弃元素 dropWhile
从 Stream 开头丢弃满足条件的元素,直到第一个不满足条件的元素后保留剩余元素。也只作用于顺序流时效果一致。
import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of(1, 2, 3, 4, 1, 2) .dropWhile(n -> n < 4) // 条件满足时丢弃元素 .forEach(System.out::println); // 输出: 4 1 2 } }
高性能一对多映射 mapMulti
JDK21引入,替代
flatMap,避免创建中间 Stream。import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream.of("苹果", "香蕉") .mapMulti((str, consumer) -> { for (char c : str.toCharArray()) { consumer.accept(c); // 输出每个字符 } }) .forEach(System.out::println); // 输出: a p p l e b a n a n a } }
Stream 终止操作
执行终止操作会触发 Stream 的计算,Stream 不可复用。
操作 返回值 功能与说明 forEach void 遍历每个元素并执行动作,常用于打印或输出 toArray 数组 将 Stream 元素收集到数组中 reduce Optional / T / U 归约操作,将元素合并为一个值(求和、求最大值等) collect 任意 收集 Stream 元素到集合、Map 或自定义容器 count long 返回元素数量 anyMatch boolean 判断是否至少有一个元素匹配条件 allMatch boolean 判断是否所有元素都匹配条件 noneMatch boolean 判断是否没有元素匹配条件 findFirst Optional 返回第一个元素 findAny Optional 返回任意一个元素(顺序流通常是第一个,并行流可能是任意) max/min Optional 返回最大值或最小值
遍历 forEach
- 遍历每个元素并执行指定动作,常用于打印、日志或其他副作用操作。
- 不能返回值。
- 对并行流,元素处理顺序可能不固定。
import java.util.List; import java.util.Arrays; public class StreamForEachDemo { public static void main(String[] args) { List<String> list = Arrays.asList("Java", "Python", "C++"); // 遍历输出每个元素 list.stream() .forEach(System.out::println); } }
匹配 Match & Find
- 匹配操作用于判断 Stream 中元素是否满足指定条件,或者获取特定元素。
allMatch、anyMatch、noneMatch短路操作,一旦结果确定就会停止遍历,性能高。findFirst在顺序流中返回第一个元素,在并行流中也保证顺序;findAny在并行流中可能返回任意元素。- 返回值为
Optional的方法应避免直接get(),推荐使用ifPresent或orElse避免空指针异常。
方法 返回值 功能说明 allMatch boolean 判断是否所有元素都匹配条件 anyMatch boolean 判断是否至少有一个元素匹配条件 noneMatch boolean 判断是否没有元素匹配条件 findFirst Optional 获取第一个元素 findAny Optional 获取任意一个元素(并行流可能不同) import java.util.List; import java.util.Arrays; import java.util.Optional; public class StreamMatchFindDemo { public static void main(String[] args) { List<String> list = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve"); // 1. 检查是否所有名字长度大于 2 boolean all = list.stream() .allMatch(name -> name.length() > 2); System.out.println("是否所有名字长度大于 2:" + all); // 2. 检查是否至少有一个名字以 'A' 开头 boolean any = list.stream() .anyMatch(name -> name.startsWith("A")); System.out.println("是否至少有一个名字以 A 开头:" + any); // 3. 检查是否没有名字为空 boolean none = list.stream() .noneMatch(String::isEmpty); System.out.println("是否没有名字为空:" + none); // 4. 获取第一个名字 Optional<String> first = list.stream().findFirst(); first.ifPresent(s -> System.out.println("第一个名字:" + s)); // 5. 获取任意一个名字(并行流可能随机) Optional<String> anyName = list.parallelStream().findAny(); anyName.ifPresent(s -> System.out.println("任意一个名字:" + s)); // 6. 跳过前 3 个名字,获取第 4 个 Optional<String> fourth = list.stream().skip(3).findFirst(); fourth.ifPresent(s -> System.out.println("第四个名字:" + fourth.get())); } }
归约 reduce
- 归约(reduce)用于将 Stream 中的元素按照一定规则组合,最终生成一个值。
- 在 Java Stream 接口中,常用归约方法如下:
Optional<T> reduce(BinaryOperator<T> accumulator):没有初始值,返回 Optional,适用于可能为空的流。T reduce(T identity, BinaryOperator<T> accumulator):提供初始值,返回类型与元素类型相同,适用于有初始值的累加或累乘等操作。- 常见的
max()、min()、count()也可以视作特殊的归约操作:
long count():计算元素数量Optional<T> max(Comparator<? super T> comparator):获取最大元素Optional<T> min(Comparator<? super T> comparator):获取最小元素import java.util.Arrays; import java.util.List; import java.util.Optional; public class StreamReduceDemo { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 1. 所有元素相加 Integer sum = numbers.stream() .reduce(Integer::sum) // 等同于 (x, y) -> x + y .orElse(0); System.out.println("总和:" + sum); // 输出: 15 // 2. 所有元素相乘 Integer product = numbers.stream() .reduce((x, y) -> x * y) .orElse(1); System.out.println("乘积:" + product); // 输出: 120 // 3. 找到最大元素 Integer max = numbers.stream() .reduce(Integer::max) .orElse(null); System.out.println("最大值:" + max); // 输出: 5 // 4. 找到最小元素 Integer min = numbers.stream() .reduce(Integer::min) .orElse(null); System.out.println("最小值:" + min); // 输出: 1 // 5. 使用初始值进行累加(例如初始值为 10) Integer sumWithInit = numbers.stream() .reduce(10, Integer::sum); System.out.println("总和(含初始值10):" + sumWithInit); // 输出: 25 // 6. 使用 count() 获取元素数量 long count = numbers.stream().count(); System.out.println("元素数量:" + count); // 输出: 5 // 7. 使用 max() 获取最大元素 Optional<Integer> maxValue = numbers.stream().max(Integer::compareTo); maxValue.ifPresent(val -> System.out.println("最大值(max()):" + val)); // 8. 使用 min() 获取最小元素 Optional<Integer> minValue = numbers.stream().min(Integer::compareTo); minValue.ifPresent(val -> System.out.println("最小值(min()):" + val)); } }
收集 collect
collect是最灵活的终止操作,可将 Stream 的元素归集到集合、Map等。
归集(toList/toSet/toMap)
toList()/toSet()仅返回接口类型(List / Set),具体实现不可控。- 如果想明确集合类型,如
ArrayList、LinkedList、HashSet、TreeSet,需使用toCollection。import java.util.*; import java.util.stream.Collectors; public class StreamCollectDemo { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "orange", "apple"); // 转为 List List<String> list = words.stream() .collect(Collectors.toList()); System.out.println("List: " + list); // 转为 Set(去重) Set<String> set = words.stream() .collect(Collectors.toSet()); System.out.println("Set: " + set); // 转为指定集合类型 - ArrayList ArrayList<String> arrayList = words.stream() .collect(Collectors.toCollection(ArrayList::new)); System.out.println("ArrayList: " + arrayList); // 转为指定集合类型 - LinkedList LinkedList<String> linkedList = words.stream() .collect(Collectors.toCollection(LinkedList::new)); System.out.println("LinkedList: " + linkedList); // 转为指定集合类型 - TreeSet(排序去重) TreeSet<String> treeSet = words.stream() .collect(Collectors.toCollection(TreeSet::new)); System.out.println("TreeSet: " + treeSet); // 转 Map - key 为首字母,value 为单词 Map<Character, String> map = words.stream() .collect(Collectors.toMap( w -> w.charAt(0), w -> w, (existing, replacement) -> existing // 遇到重复 key 时保留已有值 )); System.out.println("Map: " + map); } }
数组 toArray
toArray()返回Object[]。toArray(T[]::new)可返回指定类型数组。import java.util.*; import java.util.stream.Collectors; public class StreamToArrayDemo { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "orange", "apple"); // 转为 String 数组 String[] array1 = words.stream() .toArray(String[]::new); System.out.println("Array: " + Arrays.toString(array1)); // 转为 Object 数组 Object[] array2 = words.stream() .toArray(); System.out.println("Object Array: " + Arrays.toString(array2)); } }
分组 groupingBy
- 按指定规则对元素分组,返回
Map<K, List<T>>,K 为分组条件。import java.util.*; import java.util.stream.Collectors; public class StreamGroupingDemo { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "orange", "apricot"); // 按首字母分组 Map<Character, List<String>> grouped = words.stream() .collect(Collectors.groupingBy(w -> w.charAt(0))); System.out.println("Grouped: " + grouped); } }
连接 joining
- 将元素按指定分隔符拼接为字符串,可指定前缀和后缀。
import java.util.*; import java.util.stream.Collectors; public class StreamJoiningDemo { public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "orange", "apple"); // 使用 Collectors.joining 拼接字符串 String joined = words.stream() .collect(Collectors.joining(", ", "[", "]")); System.out.println("Joined: " + joined); // 输出: [apple, banana, orange, apple] } }
统计
Collectors提供了一系列用于数据统计的静态方法:
- 计数:
counting- 平均值:
averagingInt、averagingLong、averagingDouble- 最值:
maxBy、minBy- 求和:
summingInt、summingLong、summingDouble- 统计以上所有:
summarizingInt、summarizingLong、summarizingDoubleimport java.util.*; import java.util.stream.Collectors; public class StreamStatisticsDemo { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50); // 计数 long count = numbers.stream().collect(Collectors.counting()); System.out.println("Count: " + count); // 求平均值 double avg = numbers.stream().collect(Collectors.averagingInt(Integer::intValue)); System.out.println("Average: " + avg); // 求总和 long sum = numbers.stream().collect(Collectors.summingLong(Integer::longValue)); System.out.println("Sum: " + sum); // 最大值 Optional<Integer> max = numbers.stream().collect(Collectors.maxBy(Integer::compareTo)); max.ifPresent(v -> System.out.println("Max: " + v)); // 最小值 Optional<Integer> min = numbers.stream().collect(Collectors.minBy(Integer::compareTo)); min.ifPresent(v -> System.out.println("Min: " + v)); // 总结统计信息 IntSummaryStatistics stats = numbers.stream().collect(Collectors.summarizingInt(Integer::intValue)); System.out.println("Summary: " + stats); } }
并行流与性能优化
并行流(Parallel Stream)通过多线程并行处理元素,可以提升 CPU 密集型任务的性能。
创建并行流
import java.util.Arrays; public class ParallelStreamDemo { public static void main(String[] args) { // 方法1:通过集合 parallelStream() 创建并行流 Arrays.asList(1, 2, 3, 4, 5, 6) .parallelStream() .forEach(System.out::println); // 方法2:将普通流转换为并行流 Arrays.asList(1, 2, 3, 4, 5, 6) .stream() .parallel() .forEach(System.out::println); } }
并行流的注意事项
- 适合 CPU 密集型操作
- 大量计算、数据处理时,使用并行流可以显著提升性能。
- I/O 密集型或小数据量时,并行开销可能大于收益。
- 避免共享可变状态
- 并行流中多线程同时处理元素,如果修改共享变量可能导致线程安全问题。
- 推荐使用无状态操作或线程安全容器。
- 减少中间操作冗余
- 避免重复
distinct()、sorted()等操作,因为它们可能在并行流中开销更大。- 顺序敏感操作慎用
forEachOrdered()可以保证顺序,但会降低并行性能。
流式异常处理
Stream API 不像传统循环容易使用
try-catch,但仍可通过多种方式处理异常。注意:
- Stream 不允许抛出受检异常(Checked Exception),必须捕获或转换为运行时异常。
- 处理异常时避免中断整个流,必要时可提供默认值或跳过。
- 并行流中异常会在线程中抛出,需注意异常传播和日志记录。
- 方式1:在 lambda 内部捕获异常
import java.util.Arrays; public class Main { public static void main(String[] args) { Arrays.asList("1", "2", "a", "3") .stream() .map(s -> { try { return Integer.parseInt(s); } catch (NumberFormatException e) { System.out.println("转换失败: " + s); return 0; // 默认值或跳过处理 } }) .forEach(System.out::println); } }
- 方式2:封装受检异常(Checked Exception)
import java.util.Arrays; import java.util.stream.Stream; import java.io.IOException; public class StreamCheckedExceptionDemo { public static void main(String[] args) { String[] paths = {"file1.txt", "file2.txt"}; Arrays.stream(paths) .map(path -> { try { // 假设 readFile 可能抛 IOException return readFile(path); } catch (IOException e) { e.printStackTrace(); return ""; // 出错返回空字符串 } }) .forEach(System.out::println); } private static String readFile(String path) throws IOException { // 模拟读取文件 if ("file2.txt".equals(path)) throw new IOException("读取失败"); return "内容:" + path; } }
- 方式3:将异常包装为 RuntimeException
Arrays.asList("1", "2", "a", "3") .stream() .map(s -> { try { return Integer.parseInt(s); } catch (NumberFormatException e) { throw new RuntimeException(e); } }) .forEach(System.out::println);