使用 Java 8 中的 Stream ,可以让你写代码事半功倍

2023-09-01 16:56:46 浏览数 (2)

Stream

Java 8 中一个主要的新功能是引入了流(Stream)功能。在java.util.stream中包含用于处理元素序列的类。其中,最重要的类是Stream<T>。下面我们就来看看如何使用现有的数据源创建流。

创建Stream

可以使用 stream()of() 方法从不同的数据源(例如:集合、数组)创建流:

代码语言:javascript复制
String[] arr = new String[]{"万", "猫", "学", "社"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("万", "猫", "学", "社");

Collection 接口新增了一个 stream() 默认方法,允许使用任何集合作为数据源来创建 Stream<T>

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("万");
list.add("猫");
list.add("学");
list.add("社");
Stream<String> stream = list.stream();

在多线程中使用Stream

Stream 还通过提供 parallelStream() 方法来简化多线程操作,该方法以并行模式运行对流元素的操作。

下面的代码可以对流的每个元素并行运行 doWork() 方法:

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("万");
list.add("猫");
list.add("学");
list.add("社");
list.parallelStream().forEach(element -> doWork(element));

接下来,我们将介绍一些基本的 Stream 操作。

Stream 操作

在流上可以执行许多有用的操作。它们被分为中间操作(返回 Stream<T>)和终端操作(返回明确定义类型的结果),中间操作允许链接。

我需要注意的是,流上的操作不会改变数据源。

下面是一个简单的例子:

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("万");
list.add("猫");
list.add("学");
list.add("社");
long count = list.stream().distinct().count();

在上面的例子中,distinct() 方法表示一个中间操作,它创建了前一个流的唯一元素的新流。而 count() 方法是一个终端操作,它返回流的大小。

迭代

Stream 帮助我们替代了 for、for-each 和 while 循环。它可以让我们把精力集中在操作的逻辑上,而不是在迭代元素序列上。

比如下面的代码:

代码语言:javascript复制
for (String string : list) {
    if (string.contains("猫")) {
        return true;
    }
}

这段代码只需要一行 Stream 代码就可以实现:

代码语言:javascript复制
boolean isExist = list.stream().anyMatch(element -> element.contains("猫"));

过滤

filter() 方法可以让我们选择满足谓词条件的元素流。

比如下面的代码:

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("万");
list.add("猫");
list.add("学");
list.add("社");
Stream<String> stream = list.stream().filter(element -> element.contains("猫"));

在上面的例子中,创建了一个 List<String>Stream<String>,查找该流中所有包含字符“猫”的元素,并创建一个只包含筛选后元素的新流。

映射

为了通过将特殊函数应用于流元素来转换它们,并将这些新元素收集到流中,我们可以使用 map() 方法。

比如下面的代码:

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
Stream<Integer> stream = list.stream().map(str -> Integer.valueOf(str));

在上面的例子中,通过对初始流的每个元素应用特定的 lambda 表达式将 Stream<String> 转换为 Stream<Integer>

如果您有一个流,其中每个元素都包含其自己的元素序列,并且您想创建这些内部元素的流,则应使用 flatMap() 方法。

比如下面的代码:

代码语言:javascript复制
public class Writer {
    private String name;
    private List<String> books;
}
代码语言:javascript复制
List<Writer> writers = new ArrayList<>();
writers.add(new Writer());
Stream<String> stream = writers.stream().flatMap(writer -> writer.getBooks().stream());

在上面的例子中,我们有一个类型为 Writer 的元素列表。Writer 类包含一个类型为 List<String> 的字段 books。使用 flatMap() 方法,字段 books 中的每个元素将被提取并添加到新的结果流中。之后,最开始的 Stream将会丢失。

匹配

Stream 提供了一组方便的工具,根据一些谓词验证一个序列的元素。我们可以使用以下方法之一:

  • anyMatch():只要有一个条件满足即返回true
  • allMatch():必须全部都满足才会返回true
  • noneMatch():全都不满足才会返回true

它们都返回 boolean 的终端操作。

比如下面的代码:

代码语言:javascript复制
List<String> list = new ArrayList();
list.add("万");
list.add("猫");
list.add("学");
list.add("社");
list.stream().anyMatch(element -> element.contains("万")); // true
list.stream().allMatch(element -> element.contains("万")); // false
list.stream().noneMatch(element -> element.contains("万")); // false

对于空的 Stream,无论给定的谓词是什么,allMatch() 方法都将返回 true:

代码语言:javascript复制
Stream.empty().anyMatch(Objects::nonNull); // false

这是一个合理的值,因为我们找不到不满足谓词的任何元素。

同样地,对于空的 Stream,anyMatch() 方法总是返回 false:

代码语言:javascript复制
Stream.empty().anyMatch(Objects::nonNull); // false

同样地,这也是合理的,因为我们找不到满足这个条件的元素。

合并

我可以使用类型为 Stream 的 reduce() 方法,根据指定的函数将一系列元素合并为某个值。这个方法有两个参数:第一个是起始值,第二个是累加器函数。

比如下面的代码:

代码语言:javascript复制
List<Integer> integers = Arrays.asList(1, 2, 3);
Integer reduced = integers.stream().reduce(4, (a, b) -> a   b);

在上面的例子中,有一个 List<Integer>,我们将这些元素加起来,并加上一个初始的整数(在这个例子中是4)。那么,运行以下代码的结果是10(4 1 2 3)。

收集

在 Stream 类型中,也可以通过 collect() 方法来进行收集。这个操作非常方便,可以将一个流转换为 CollectionMap,也可以将一个流表示为单个字符串。Collectors 是一个实用类,提供了几乎所有典型的收集操作的解决方案。对于一些不太常见的任务,可以创建自定义的收集器。

下面的代码使用终端操作 collect()Stream<String> 转换为 List<String>

代码语言:javascript复制
List<String> resultList 
  = list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());

最后

Stream 的高级示例非常丰富,本文的目的是为了让我们快速了解 Stream 功能的用法,并启发我们继续探索和深入学习。

Stream 是 Java 8 中非常强大和实用的 API,它为开发人员提供了一种更加简便的方式来处理数据。希望我们通过本文的介绍和示例,可以快速上手使用 Stream,并继续深入学习和探索。

0 人点赞