Java 8引入的Stream API为处理集合数据提供了一种便利而强大的方式。深入理解Java 8的Stream流包括对Stream的基本概念、操作类型和工作原理的理解。
1. 基本概念:
1.1 Stream
:
Stream
是Java 8中处理集合的新抽象,它允许你以声明性的方式处理数据。Stream
不是一个数据结构,而是对数据进行操作的一种视图。Stream
操作可以是中间操作或终端操作,中间操作返回一个新的Stream
,终端操作返回一个最终结果。
1.2 中间操作和终端操作:
-
中间操作(Intermediate Operations): 这些操作会返回一个新的
Stream
,允许你在原始Stream
的元素上进行一系列的转换和过滤。例如,filter
、map
等。 -
终端操作(Terminal Operations): 这些操作会触发
Stream
的处理,并生成最终的结果。终端操作是Stream
流的最后一个操作。例如,forEach
、collect
、reduce
等。
2. 操作类型:
2.1 过滤与切片:
filter(Predicate<T> predicate)
:通过谓词(Predicate)过滤元素。List<String> filteredList = myList.stream() .filter(s -> s.startsWith("A")) .collect(Collectors.toList());
2.2 映射:
map(Function<T, R> mapper)
:对每个元素应用一个函数。List<Integer> lengthList = myList.stream() .map(String::length) .collect(Collectors.toList());
2.3 扁平化:
flatMap(Function<T, Stream<R>> mapper)
:将每个元素映射为一个Stream
,然后合并这些Stream
。List<String> flatMapList = myList.stream() .flatMap(s -> Arrays.stream(s.split(""))) .collect(Collectors.toList());
2.4 排序:
sorted()
:对元素进行自然排序。sorted(Comparator<T> comparator)
:使用自定义比较器进行排序。List<String> sortedList = myList.stream() .sorted() .collect(Collectors.toList());
2.5 匹配与查找:
anyMatch(Predicate<T> predicate)
:判断是否至少匹配一个元素。allMatch(Predicate<T> predicate)
:判断是否全部匹配元素。noneMatch(Predicate<T> predicate)
:判断是否没有匹配元素。findFirst()
:返回第一个元素。findAny()
:返回任意一个元素。boolean anyStartsWithA = myList.stream().anyMatch(s -> s.startsWith("A")); Optional<String> firstElement = myList.stream().findFirst();
2.6 归约:
reduce(T identity, BinaryOperator<T> accumulator)
:从流中的第一个元素开始,进行连续的归约操作。Optional<String> concatenatedString = myList.stream() .reduce((s1, s2) -> s1 + s2);
3. 工作原理:
-
惰性求值:
Stream
是惰性求值的,只有终端操作被触发时,中间操作才会执行。这样可以避免对整个集合进行操作,提高效率。 -
并行流:
Stream
提供了并行处理集合的功能,通过parallel()
方法可以将顺序流转为并行流。但在某些情况下,并行流可能不如顺序流效率高,要谨慎使用。List<String> parallelList = myList.parallelStream() .filter(s -> s.startsWith("A")) .collect(Collectors.toList());
4. 收集器(Collector):
Collectors
类提供了很多静态方法,用于生成各种常用的Collector
实现,用于对Stream
的元素进行汇总。toList()
:将元素收集到一个List
中。toSet()
:将元素收集到一个Set
中。toMap(keyMapper, valueMapper)
:将元素收集到一个Map
中。List<String> list = myList.stream().collect(Collectors.toList()); Set<String> set = myList.stream().collect(Collectors.toSet()); Map<String, Integer> map = myList.stream() .collect(Collectors.toMap(Function.identity(), String::length));
5. 分组与分区:
groupingBy(classifier)
:根据分类器对元素进行分组,返回一个Map
。partitioningBy(predicate)
:根据谓词进行分区,返回一个Map
,其中键是Boolean值。Map<Integer, List<String>> groupedByLength = myList.stream() .collect(Collectors.groupingBy(String::length)); Map<Boolean, List<String>> partitioned = myList.stream() .collect(Collectors.partitioningBy(s -> s.length() > 3));
6. 连接字符串:
joining()
:将Stream
的元素连接成一个字符串。String result = myList.stream().collect(Collectors.joining(", "));
7. 自定义收集器:
你也可以创建自定义的收集器,实现Collector
接口中的方法,用于灵活地对元素进行汇总。
public class StringCollector implements Collector<String, StringBuilder, String> {
@Override
public Supplier<StringBuilder> supplier() {
return StringBuilder::new;
}
@Override
public BiConsumer<StringBuilder, String> accumulator() {
return StringBuilder::append;
}
@Override
public BinaryOperator<StringBuilder> combiner() {
return StringBuilder::append;
}
@Override
public Function<StringBuilder, String> finisher() {
return StringBuilder::toString;
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
}
使用自定义收集器:
String result = myList.stream().collect(new StringCollector());
8. 并行流的注意事项:
- 共享变量: 在并行流中,要避免共享可变状态的变量,以免发生竞争条件。
- 有状态操作: 某些操作是有状态的,如
distinct()
、sorted()
,在并行流中可能导致意外结果,要注意使用。List<String> parallelList = myList.parallelStream() .filter(s -> s.startsWith("A")) .collect(Collectors.toList());
-
数据规模: 并行流在数据规模较大时才能发挥优势,对于小规模数据集,顺序流可能更有效。
-
操作成本: 有些操作在并行流中的成本可能比在顺序流中高,比如
reduce
操作。 -
数据结构: 对于某些数据结构,如
ArrayList
,并行流的性能较好。但对于LinkedList
等非线性的数据结构,可能性能不如顺序流。
10. 常见错误和注意事项:
- 不可重用:
Stream
是一次性的,一旦使用完毕就不能再次使用。如果需要多次操作,需要创建新的Stream
。Stream<String> stream = myList.stream(); stream.filter(s -> s.startsWith("A")).collect(Collectors.toList()); // 正确 stream.map(String::toUpperCase).collect(Collectors.toList()); // 错误
- 操作顺序: 在进行多个中间操作时,操作的顺序可能影响最终的结果。例如,
map
和filter
的顺序可能影响性能。List<String> result = myList.stream() .map(String::toUpperCase) .filter(s -> s.startsWith("A")) .collect(Collectors.toList());
- 空指针异常: 在使用
Stream
时,注意空指针异常的可能性。可以使用Optional
类型来避免。Optional<String> optional = myList.stream().findFirst(); optional.ifPresent(System.out::println);
11. CompletableFuture 异步编程:
CompletableFuture
是Java 8引入的用于异步编程的工具类,支持非阻塞式的编程。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, ")
.thenApplyAsync(s -> s + "World")
.thenAccept(System.out::println);
future.join(); // 阻塞等待异步任务完成
深入理解Java 8的Stream流涉及到更多的细节和高级用法,上述是其中一部分基础概念和操作类型的介绍。通过实际应用和进一步阅读文档,你可以更全面地理解