持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
初识
笔者认为觉得api文档才是最专业说明,所以先翻译下文档。
为了执行计算,流操作被组合成一个流管道。流管道由源(可能是数组、集合、生成器函数、I/O通道等)、零个或多个中间操作(将流转换成另一个流,例如filter(Predicate)),和末端操作(产生结果或副作用,例如count()或者forEach(Consumer))。流是懒惰的;仅在发起末端操作时才对原数据进行计算,并且仅在需要时消耗源元素。
集合和流虽然有一些表面上的相似之处,但有不同的目标。集合主要关注其元素的有效管理和访问。相比之下,流不提供直接访问或操作其元素的方法,而是以声明的方式描述其源以及在源上执行的聚合操作。但是,如果提供的流操作不提供所需的功能,则BaseStream.iterator()和BaseStream.spliterator()操作可用于执行受控遍历。
一个流应该只被操作一次(调用一个中间或末端流操作)。例如,“分叉”流,其中相同的源提供两个或多个管道,或同一个流的多次遍历。如果流检测到被重用将抛出IllegalStateException。但是,由于某些流操作可能返回其接收者而不是新的流对象,因此可能无法在所有情况下检测重用。
流管道可以顺序或并行执行。这种执行模式时流的属性。流是通过初始选择顺序或并行执行来创建。(例如,Collection.stream()创建一个顺序流,Collection.parallelStream()创建一个并行流。)这种执行模式的选择可以通过 BaseStream.sequential()或者BaseStream.parallel()方法进行修改,并且可以通过该方法进行查询BaseStream.isParallel()。
我们从以下几个点去了解下:
- 为啥要使用Stream
- 如何创建流
- 流的中间操作使用
- 流的末端操作使用
- 流的并行执行如何使用
- 并行是怎么实现的
- Stream的底层是怎么实现的
- 流的惰性执行是怎么实现的
体验一下
从给定句子中返回单词长度大于5的单词列表,按长度倒序输出,最多返回3个
- Java7及以前
public static List<String> sortGetTop3LongWords(String sentence) {
String[] words = sentence.split(" ");
List<String> wordList = new ArrayList<>();
for (String word : words) {
if (word.length() >= 5) {
wordList.add(word);
}
}
wordList.sort(((o1, o2) -> o2.length() - o1.length()));
if (wordList.size() > 3) {
wordList = wordList.subList(0, 3);
}
return wordList;
}
- Java8及以后
public static List<String> sortGetTop3LongWordsByStream(String sentence) {
return Arrays.stream(sentence.split(" ")).filter(word -> word.length() > 5)
.sorted((o1, o2) -> o2.length() - o1.length()).limit(3).collect(Collectors.toList());
}
第一感受就是变简洁了许多,而且如果对Stream API稍有了解的话,笔者认为易读性更好。
如何创建流
Collection
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();//顺序流
Stream<String> stringStream = list.parallelStream();//并行流
数组转成流
Stream<Integer> integerStream = Arrays.stream(new Integer[]{1, 2, 3, 4});
Stream中的静态方法
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4);
// iterate()、generate()可以创建无限流,可以通过limit()方法限制数量
Stream<Integer> stream2 = Stream.generate(() -> new Random().nextInt(10)).limit(10);
Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2).limit(10);
BufferedReader.lines()方法
Stream<String> linesStream = new BufferedReader(new FileReader("/Users/xxx/Downloads/test")).lines();
Pattern.splitAsStream()方法
Pattern pattern=Pattern.compile(",");
Stream<String> stringStream1 = pattern.splitAsStream("a,b,c,d");
流的中间操作使用
filter
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
integerStream.filter(i -> i % 2 == 0);
integerStream.forEach(System.out::println);
这里会报错:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.study.stream.IntermediateOperations.main(IntermediateOperations.java:9)
改下:
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> streamFilter = integerStream.filter(i -> i % 2 == 0);
streamFilter.forEach(System.out::println);
原因是因为流只能被操作一次,上面“初识”章节中也提到了。
skip & limit
- skip(n):跳过n元素,配合limit(n)可实现分页。
- limit(n):限制获取元素的个数。
Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 4, 5, 6);
integerStream1.skip(3).limit(3).forEach(integer -> System.out.print(integer + " "));
distinct
去重
Stream<Integer> integerStream2 = Stream.of(1, 2, 3, 6, 6, 6);
integerStream2.distinct().forEach(integer -> System.out.print(integer + " "));
map
逐个对每个元素进行映射成新的元素。
Stream<Integer> integerStream3 = Stream.of(1, 2, 3, 4, 5, 6);
integerStream3.map(integer -> integer+1).forEach(integer -> System.out.print(integer + " "));
System.out.println();
flatmap
平铺映射,逐个将每个元素拆成stream,最终合成一个stream。
Stream<List<Integer>> listStream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
listStream.flatMap(list-> list.stream()).forEach(integer -> System.out.print(integer + " "));
sorted
可以调用sorted(Ljava/util/Comparator)Ljava/util/stream/Stream实现Comparator匿名内部类,如果流中的元素实现了Comparable接口也可以直接调用调用sorted()。
public static void main(String[] args) {
Stream<Person> listStream1 = Stream.of(new Person("wf", 1), new Person("fw", 3), new Person("fw", 2));
listStream1.sorted(((o1, o2) -> {
if (o1.name.equals(o2.name)) {
return o1.age - o2.age;
} else {
return o1.name.compareTo(o2.name);
}
})).forEach(System.out::println);
}
@AllArgsConstructor
@ToString
static class Person {
private String name;
private Integer age;
}
peek
和map的不同在于map是Function<? super T, ? extends R> mapper有返回,peek是Consumer<? super T> action没有返回。
Stream<Integer> integerStream4 = Stream.of(1, 2, 3, 4, 5, 6);
integerStream4.peek(System.out::println).collect(Collectors.toList());
注意因为peek是中间操作,所以直接peek不会执行。
流的末端操作使用
match
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list.stream().allMatch(integer -> integer > 3));
System.out.println(list.stream().noneMatch(integer -> integer > 5));
System.out.println(list.stream().anyMatch(integer -> integer > 3));
输出:
false
true
true
find
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list.stream().findFirst().get());
System.out.println(list.stream().findAny().get());
输出:
1
1
count
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list.stream().count());
max
System.out.println(list.stream().max(Integer::compareTo).get());
min
System.out.println(list.stream().min(Integer::compareTo).get());
reduce
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(list.stream().reduce((a, b) -> a + b).get());
输出:
15
- 给定初始值
System.out.println(list.stream().reduce(100,(a, b) -> a + b));
collect
List<Person> peoples = new ArrayList<Person>() {
{
{
add(new Person("wf", 1));
add(new Person("fw", 3));
add(new Person("w", 2));
}
}
};
//转成map
System.out.println(peoples.stream().collect(Collectors.toMap(Person::getName, Person::getAge)));
//拼接
System.out.println(peoples.stream().map(Person::getName).collect(Collectors.joining(",")));
//统计
System.out.println(peoples.stream().collect(Collectors.counting()));
//最大
System.out.println(peoples.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compare)).get());
//所有人的年龄求和
System.out.println(peoples.stream().collect(Collectors.summingInt(Person::getAge)));
//平均年龄
System.out.println(peoples.stream().collect(Collectors.averagingInt(Person::getAge)));
IntSummaryStatistics statistics = peoples.stream().collect(Collectors.summarizingInt(Person::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
//按年龄分组
System.out.println(peoples.stream().collect(Collectors.groupingBy(Person::getAge)));
// 分区 分成两部分,一部分大于2,一部分小于等于2
peoples.stream().collect(Collectors.partitioningBy(p -> p.getAge() > 2));
// reducing sum
System.out.println(peoples.stream().map(Person::getAge).collect(Collectors.reducing(Integer::sum)).get());