一篇文章带你学会Java的stream
Java 8 引入了一系列重要的新特性,其中之一便是 Stream API。Stream API 是一个用于处理集合数据的强大工具,通过提供一种简洁、高效且易于并行化的方式来处理数据流,从而极大地简化了编写代码的复杂性。本文将深入探讨 Java 8 Stream API 的基本概念、使用方法以及实际应用。
以下是对stream的介绍,你可以根据文章中所提到的代码块,直接放到main方法中跑,可以直观的看出stream的神奇之处
一、什么是 Stream API?
1.1 基本概念
Stream API 是 Java 8 提供的一个新特性,它不是数据结构,而是关于数据计算的抽象。Stream 代表数据通道,可以在其中执行一系列操作来处理数据。其设计灵感来源于函数式编程,通过流操作可以对数据进行过滤、映射、规约等操作。
1.2 Stream 和传统集合的区别
传统集合框架(如 List、Set)主要关注数据的存储和访问,而 Stream 更关注数据的计算过程。集合是数据的静态存储,而 Stream 是数据的动态视图,通过一系列的操作(intermediate operations)和终端操作(terminal operations)进行计算。
二、Stream 的基本操作
2.1 创建 Stream
Stream 可以通过多种方式创建,常见的方法有:
- 从集合创建
- 从数组创建
- 使用工厂方法(如 Stream.of) 生成无限流
2.1.1 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
2.1.2 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
2.1.3 使用工厂方法
Stream<String> stream = Stream.of("a", "b", "c");
2.1.4 生成无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
2.2 中间操作(Intermediate Operations)
中间操作是对流中的元素进行加工和转换,并生成一个新的流。常见的中间操作包括 filter、map、flatMap、distinct、sorted 和 peek。
2.2.1 filter(过滤)
filter 方法用于过滤符合条件的元素。
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream<String> stream = list.stream().filter(s -> s.startsWith("a"));
2.2.2 map(分类)
map 方法用于将元素转换为另一种形式。
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream<String> stream = list.stream().map(String::toUpperCase);
2.2.3 flatMap(分类)
flatMap 方法用于将每个元素转换为流,然后合并成一个新的流。
List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
Stream<String> stream = listOfLists.stream().flatMap(Collection::stream);
2.2.4 distinct(去重)
distinct 方法用于去重。
List<String> list = Arrays.asList("a", "b", "a", "c");
Stream<String> stream = list.stream().distinct();
2.2.5 sorted(排序)
sorted 方法用于对元素进行排序。
List<String> list = Arrays.asList("d", "a", "c", "b");
Stream<String> stream = list.stream().sorted();
2.2.6 peek(监视)
peek 方法用于在中间操作中对每个元素执行某个动作。
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream<String> stream = list.stream().peek(System.out::println);
2.3 终端操作(Terminal Operations)
终端操作是流的最后一步操作,它会触发流的处理,并产生最终的结果。常见的终端操作包括 forEach、collect、reduce、count、anyMatch、allMatch 和 noneMatch。
2.3.1 forEach(遍历)
forEach 方法用于对流中的每个元素执行某个动作。
List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().forEach(System.out::println);
2.3.2 collect(收集)
collect 方法用于将流中的元素收集到集合中。
List<String> list = Arrays.asList("a", "b", "c", "d");
List<String> result = list.stream().collect(Collectors.toList());
2.3.3 reduce(重组)
reduce 方法用于将流中的元素组合成一个结果。
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = list.stream().reduce(0, Integer::sum);
2.3.4 count(计数)
count 方法用于计算流中的元素个数。
List<String> list = Arrays.asList("a", "b", "c", "d");
long count = list.stream().count();
2.3.5 anyMatch(判断 任一)
anyMatch 方法用于判断是否有任意一个元素满足条件。
List<String> list = Arrays.asList("a", "b", "c", "d");
boolean anyMatch = list.stream().anyMatch(s -> s.equals("a"));
2.3.6 allMatch(判断 全部)
allMatch 方法用于判断是否所有元素都满足条件。
List<String> list = Arrays.asList("a", "b", "c", "d");
boolean allMatch = list.stream().allMatch(s -> s.length() == 1);
2.3.7 noneMatch(判断 非)
noneMatch 方法用于判断是否没有元素满足条件。
List<String> list = Arrays.asList("a", "b", "c", "d");
boolean noneMatch = list.stream().noneMatch(s -> s.length() > 1);
三、并行流(Parallel Stream)
Stream API 提供了对流进行并行处理的能力,通过 parallelStream 方法或 parallel 方法,可以轻松地将流转换为并行流。
3.1 使用 parallelStream
List<String> list = Arrays.asList("a", "b", "c", "d");
list.parallelStream().forEach(System.out::println);
3.2 使用 parallel 方法
List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().parallel().forEach(System.out::println);
3.3 并行流的性能考虑
虽然并行流可以提升性能,但并不是所有情况下都适用。使用并行流时需要考虑以下几点:
- 数据量:并行流适用于大数据量处理,数据量较小时,并行化的开销可能超过其带来的性能提升。
- 数据结构:某些数据结构(如 ArrayList)在并行流中表现更好,而另一些(如 LinkedList)则可能不适合。
- 算法复杂度:复杂度较高的操作可能更适合并行流,但需要确保线程安全。
四、Stream API 实际应用
4.1 数据过滤与转换
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = list.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(result);
4.2 数据分组与统计
List<String> list = Arrays.asList("apple", "banana", "cherry", "date", "apricot");
Map<Character, List<String>> groupedByFirstLetter = list.stream()
.collect(Collectors.groupingBy(s -> s.charAt(0)));
System.out.println(groupedByFirstLetter);
4.3 查找最大值和最小值
List<Integer> list = Arrays.asList(10, 20, 30, 40);
int max = list.stream().max(Integer::compare).orElseThrow(NoSuchElementException::new);
int min = list.stream().min(Integer::compare).orElseThrow(NoSuchElementException::new);
System.out.println("Max: " + max + ", Min: " + min);
4.4 数据聚合
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, Integer::sum);
System.out.println("Sum: " + sum);
4.5 并行流应用示例
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.parallelStream().reduce(0, Integer::sum);
System.out.println("Sum (parallel): " + sum);
五、Stream API 的高级特性
5.1 自定义收集器
Stream API 提供了 Collector 接口,允许用户自定义收集逻辑。
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
Collector<String, StringJoiner, String> customCollector = Collector.of(
() -> new StringJoiner(", "),
StringJoiner::add,
StringJoiner::merge,
StringJoiner::toString
);
String result = list.stream().collect(customCollector);
System.out.println(result);
5.2 使用流构建器
Stream API 提供了 Stream.Builder 类,允许用户逐步构建流。
Stream.Builder<String> builder = Stream.builder();
builder.add("a").add("b").add("c");
Stream<String> stream = builder.build();
stream.forEach(System.out::println);
5.3 流的短路操作
某些操作(如 anyMatch、allMatch、noneMatch、findFirst、findAny)是短路操作,即在条件满足后会立即结束处理,不再处理剩余元素。
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("a"));
System.out.println("Any match: " + anyMatch);
六、结论
Java 8 的 Stream API 是一个强大的工具,为数据处理提供了简洁、高效的方式。通过理解 Stream 的基本概念和操作方法,我们可以编写出更加简洁和高效的代码。尽管 Stream 提供了很多便利,但在使用过程中仍需注意性能和可读性,确保代码不仅高效,而且易于维护。
希望本文能帮助你更好地理解和使用 Java 8 的 Stream API,并在实际开发中充分发挥其优势。