Java Lamda Stream API
现在我们通过本文学习一下Java 8 Streams从创建到并行执行的实际用法。
Java 8的主要新特性之一是引入了流功能--java.util.stream,它包含了处理元素序列的类。
核心API类是Stream<T>。
1 Stream创建
在stream()和of()方法的帮助下,可以从不同的元素源(如集合或数组)创建Stream。
String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");
在Collection接口中添加了一个默认的stream()方法,允许使用任何集合作为元素源创建一个Stream<T>。
Stream<String> stream = list.stream();
2 多线程与Stream
Stream API还通过提供parallelStream()方法简化了多线程,该方法以并行模式运行对流元素的操作。
下面的代码允许对流的每个元素并行运行doWork()方法。
list.parallelStream().forEach(element -> doWork(element));
3 Stream 操作
有许多有用的操作可以在流上执行,它们分为中间操作(返回Stream<T>)和终端操作(返回确定类型的结果)。
中间操作允许链式操作。
另外值得注意的是,对Stream的操作不会改变源数据。
long count = list.stream().distinct().count();
所以,distinct()方法代表的是一个中间操作,它由前一个流的唯一元素创建一个新的流。而count()方法是一个终端操作,它返回流的元素个数。
4 迭代
流API有助于替代for-each和while循环。它允许专注于操作的逻辑,而不是元素序列的迭代。例如:
for (String string : list) {
if (string.contains("a")) {
return true;
}
}
这段代码只要用一行Java 8代码就可以:
boolean isExist = list.stream().anyMatch(element -> element.contains("a"));
5 筛选
filter()方法允许我们选择满足条件的元素流。
例如,如以下列表:
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("OneAndOnly");
list.add("Derek");
list.add("Change");
list.add("factory");
list.add("justBefore");
list.add("Italy");
list.add("Italy");
list.add("Thursday");
list.add("");
list.add("");
下面的代码创建了一个List<String>的Stream<String>,找到这个流中所有包含char "d "的元素,并创建一个只包含过滤元素的新流。
Stream<String> stream = list.stream().filter(element -> element.contains("d"));
6 映射
要通过对Stream的元素应用一个特殊的函数进行转换,并将这些新的元素收集到一个Stream中,我们可以使用map()方法。
List<String> uris = new ArrayList<>();
uris.add("C:\\My.txt");
Stream<Path> stream = uris.stream().map(uri -> Paths.get(uri));
因此,上面的代码通过将特定的lambda表达式应用于初始Stream的每个元素,将Stream<String>转换为Stream<Path>。
如果你有一个流,在这个流中,每个元素都包含自己的元素序列,你想创建这些内部元素的流,你应该使用flatMap()方法。
List<Detail> details = new ArrayList<>();
details.add(new Detail());
Stream<String> stream
= details.stream().flatMap(detail -> detail.getParts().stream());
在这个例子中,我们有一个类型为Detail的元素列表。Detail类包含一个字段PARTS,它是一个List<String>。在flatMap()方法的帮助下,字段PARTS中的每一个元素都将被提取并添加到新产生的流中。之后,初始的Stream<Detail>将丢失。
7 匹配
Stream API提供了一套方便的工具,可以根据一些前提条件来验证一个序列的元素。要做到这一点,可以使用以下方法之一:anyMatch()、allMatch()、noneMatch()。它们的名字是不言自明的。这些都是终端操作,返回一个布尔值。
boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true
boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false
boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false
8 缩减
Stream API允许在Stream类型的reduce()方法的帮助下,根据指定的函数将一个元素序列还原成某个值。这个方法需要两个参数:第一个--起始值,第二个--累加器函数。
想象一下,你有一个List<Integer>,你想得到所有这些元素和一些初始Integer(在这个例子中为23)的总和。所以,你可以运行下面的代码,结果将是26(23+1+1+1)。
List<Integer> integers = Arrays.asList(1, 1, 1);
Integer reduced = integers.stream().reduce(23, (a, b) -> a + b);
9 收集
缩减也可以由Stream类型的collect()方法提供。这个操作在将一个流转换为一个Collection或Map,并以单个字符串的形式表示一个流的情况下非常方便。有一个实用类Collectors,它为几乎所有典型的收集操作提供了一个解决方案。对于一些非琐碎的任务,可以创建一个自定义的Collector。
List<String> resultList
= list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
这段代码使用终端的collect()操作将一个Stream<String>还原成List<String>。
10 参考
https://stackify.com/streams-guide-java-8/
https://www.baeldung.com/java-8-streams-introduction
https://www.baeldung.com/java-8-streams
- 点赞
- 收藏
- 关注作者
评论(0)