螺竹编程
发布于 2024-05-04 / 5 阅读
0

Java函数:Stream API介绍

介绍

Java中的Stream API是在Java 8中引入的一个新的API,它提供了一种更为简洁、高效和易于使用的方式来对集合和数组进行操作。Stream API可以让我们以声明式的方式来处理数据,而不是通过循环和条件语句等命令式的方式来处理数据。

Stream API提供了一种流式操作的方式,它可以对集合或数组中的元素进行过滤、映射、排序、聚合等处理操作,这些操作可以通过一系列的中间操作来构建,并以终端操作结束。中间操作会生成一个新的Stream对象,终端操作会触发Stream对象的遍历执行,产生一个最终结果或副作用。

Stream API的主要优点包括:

  1. 简洁易懂:Stream API提供了一种更为简洁、高效和易于使用的方式来对集合和数组进行操作。

  2. 并行处理:Stream API支持并行处理,可以在多核CPU上充分利用硬件资源,提高处理效率。

  3. 延迟计算:Stream API支持延迟计算,只有在需要产生结果时才会进行计算,可以节省计算资源。

  4. 可组合:Stream API的中间操作可以组合起来形成一个操作链,可以根据需要动态构建操作链。

  5. 支持丰富的操作:Stream API支持丰富的操作,包括过滤、映射、排序、聚合等常见操作,可以满足各种数据处理需求。

Java中的Stream API主要包括以下几个方面:

  1. 创建Stream:可以通过Collection接口的stream()方法、Arrays类的stream()方法、Stream类的of()方法等方式来创建Stream流。

  2. 中间操作:中间操作可以对Stream流进行转换、过滤、排序等多种操作。常见的中间操作包括map()、filter()、sorted()、distinct()等。

  3. 终止操作:终止操作可以将Stream流转换为最终结果,常见的终止操作包括forEach()、count()、reduce()、collect()等。

  4. 并行处理:Stream API可以很方便地进行并行处理,提高程序的效率。可以通过parallel()方法将Stream流转换为并行流,也可以通过sequential()方法将并行流转换为顺序流。

使用Stream API可以帮助开发者更方便地进行数据处理,避免了很多繁琐的代码,提高了程序的效率和可读性。但是需要注意,Stream API并不是适用于所有场景的,需要根据具体情况进行选择和使用。

简单示例

下面是一个简单的示例,展示了如何使用Stream API对一个整数集合进行过滤、映射和聚合操作:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sum = numbers.stream()
                .filter(n -> n % 2 == 0)
                .mapToInt(Integer::intValue)
                .sum();

System.out.println(sum); // 输出 30

在这个示例中,我们首先创建了一个包含10个整数的集合。然后使用stream()方法获取集合的流对象,接着使用filter()方法对集合进行过滤,只保留其中的偶数。然后使用mapToInt()方法将剩余的整数映射为int类型,最后使用sum()方法对映射后的整数进行求和,得到最终结果30。

创建Stream

Java的Stream API中创建Stream的方法有:

  1. 从集合(Collection)或数组(Array)中创建Stream

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> streamFromList = list.stream();

String[] array = {"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);
  1. 从指定元素创建Stream

Stream<String> streamOfElements = Stream.of("a", "b", "c");
  1. 使用Stream.generate方法创建Stream,该方法接受一个Supplier<T>类型的参数,用于生成Stream中的元素

Stream<String> streamGenerated = Stream.generate(() -> "element").limit(10);
  1. 使用Stream.iterate方法创建Stream,该方法接受一个初始值和一个UnaryOperator<T>类型的参数,用于生成Stream中的元素

Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2).limit(20);
  1. 使用Files.lines方法创建Stream,该方法接受一个文件路径作为参数,返回一个由文件中的行组成的Stream

Path path = Paths.get("file.txt");
Stream<String> streamOfFileLines = Files.lines(path);
  1. 使用Pattern.splitAsStream方法创建Stream,该方法接受一个字符串和一个正则表达式作为参数,返回一个由字符串中的匹配项组成的Stream

String str = "a-b-c";
Stream<String> streamOfStrings = Pattern.compile("-").splitAsStream(str);

中间操作

中间操作:不实际触发执行、用于构建流水线、返回Stream的操作称为中间操作。

中间操作有:distinct、sorted、skip、limit、peek、mapToLong、mapToInt、mapToDouble、flatMap等。

终端操作

终端操作:触发实际执行、返回具体结果的操作称为终端操作。

终端操作有:collect、max、min、count、allMatch、anyMatch、noneMatch、findFirst、findAny、forEach、toArray、reduce等。

Collect

在Java 8中,Stream API提供了一个collect()方法,用于将Stream中的元素收集到一个容器中,并且可以进行分组、分区、聚合等操作。collect()方法接受一个Collector对象作为参数,用于指定收集的方式。

collect()方法的基本用法是将Stream中的元素收集到一个List、Set、Map等容器中。例如,下面的示例展示了如何使用collect()方法将Stream中的元素收集到一个List中:

List<Integer> list = Stream.of(1, 2, 3, 4, 5)
                            .collect(Collectors.toList());

System.out.println(list); // 输出 [1, 2, 3, 4, 5]

在这个示例中,我们使用collect()方法将Stream中的元素收集到一个List中。Collectors.toList()方法返回一个Collector对象,用于将Stream中的元素收集到一个List中。

除了收集到List中外,collect()方法还可以将元素收集到Set、Map等容器中,也可以进行分组、分区、聚合等操作。例如,下面的示例展示了如何使用collect()方法将Stream中的元素收集到一个Map中:

Map<Integer, String> map = Stream.of("apple", "banana", "orange")
                                  .collect(Collectors.toMap(String::length, Function.identity()));

System.out.println(map); // 输出 {5=apple, 6=orange, 6=banana}

在这个示例中,我们使用collect()方法将Stream中的元素收集到一个Map中。Collectors.toMap()方法返回一个Collector对象,用于将Stream中的元素收集到一个Map中。在这个示例中,我们使用字符串长度作为键,字符串本身作为值。由于Stream中包含两个长度为6的字符串,因此这两个字符串会被合并到同一个键下。

除了toList()toMap()方法外,Collectors类还提供了许多其他的方法,用于进行分组、分区、聚合等操作,可以根据具体的需求进行选择和使用。

并行流

Java 8引入的Stream API提供了一种方便的方式来对集合进行流式处理。除了提供串行处理数据的能力之外,Stream API还支持并行处理数据,以利用多核处理器的性能优势。

在Stream API中,可以使用parallel()方法将一个串行的Stream转换为一个并行的Stream。并行处理的Stream将会在多个线程上同时执行,以加快处理速度。例如,下面的代码将对一个包含1000个元素的List进行并行处理:

List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    list.add(i);
}

Stream<Integer> parallelStream = list.parallelStream();
parallelStream.forEach(System.out::println);

在上面的代码中,parallelStream()方法将List转换为一个并行的Stream,然后使用forEach()方法遍历所有元素并打印出来。在并行处理时,Stream API会自动将数据分成若干个部分,并分配到不同的线程上进行处理。

需要注意的是,并行处理不一定总是比串行处理更快。并行处理需要额外的线程协调和通信开销,这可能会使得处理速度变慢。因此,在使用并行处理时,需要根据实际情况进行测试和优化,以获得最佳的性能。

顺序流

Stream API中的顺序流(Sequential Stream)是一种流(Stream)的类型,它按照数据源中元素的顺序依次处理每个元素。在顺序流中,处理操作是单线程执行的,因此保证了元素的处理顺序与数据源中元素的顺序相同。

可以通过调用Stream接口的sequential()方法来获得一个顺序流,例如:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> sequentialStream = numbers.stream().sequential();

在上面的例子中,我们首先通过Arrays.asList()方法创建一个包含整数的列表,然后通过stream()方法获得一个普通的流,最后通过sequential()方法将其转换为顺序流。

顺序流可以进行各种流操作,例如过滤、映射、排序等,这些操作可以通过链式编程的方式进行组合。在操作完成后,可以通过forEach()方法对流中的每个元素进行处理,例如:

sequentialStream.filter(n -> n % 2 == 0)
                .map(n -> n * n)
                .sorted()
                .forEach(System.out::println);

在上面的例子中,我们首先对流中的元素进行过滤和映射操作,然后进行排序,最后通过forEach()方法对流中的每个元素进行处理并输出到控制台上。

需要注意的是,顺序流的处理操作是单线程执行的,因此在处理大量数据时可能会出现性能瓶颈。如果需要并行处理数据,可以使用并行流(Parallel Stream)。