Stream流介绍
Stream基础概念
Stream是Java8开始引入的一个流式API,位于java.util.stream包中。记住哈,这个流可不是文件流的流,它是一个序列流,可以理解成数组、列表等这种包含的元素而形成的一个流。我们先看一个简单的stream流的写法:
java
@Test
public void basicStream() {
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
people.stream().filter(p -> p.getAge() > 20).map(p -> p.getId()).sorted().limit(100).collect(Collectors.toList()).forEach(System.out::println);
}
通过上图可以看出,一个流中间进行了若干次的计算转换,然后得到结果,Stream流的特点就是:
- Stream API提供了一套新的流式处理的抽象序列
- Stream API支持函数编程和链式操作
- Stream本身不存储元素,存储的是计算过程,只有获取结果的时候,才会进行计算然后得到结果。
Stream中的操作概述
stream中支持的方法有很多,我们将其按照中间操作和终端操作,分为两类如下:
实际业务中常见用法
Stream流的创建方式
Stream静态方法 list等转换
java
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
integerStream.forEach(System.out::println);
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
Stream<PredicateDemo.People> stream = people.stream();
Stream流中间操作
过滤 filter(Predicate<? super T> predicate)
过滤掉不符合条件的元素,筛选出符合条件的元素,如下例:filter参数接收一个Predicate参数,最终筛选的元素是predicate=true的元素。
java
@Test
public void filter(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
integerStream.filter(x -> x > 5).forEach(System.out::println); // 6 7 8 9 10
}
限制个数 limit(long maxSize)
指定获取限制个数的元素。
java
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
// 取前3个元素
Stream<Integer> stream2= stream.limit(3);
stream2.forEach((x) -> {
System.out.print(x + "\t");
});
// 2 2 3
跳过 skip(long n)
跳过n个元素,再获取,这里配合limit可以实现类似分页的场景。
java
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
// 跳过前3个元素
Stream<Integer> stream2 = stream.skip(3);
stream2.forEach((x) -> {
System.out.print(x + "\t");
});
// 7 8
去重 distinct()
对元素进行去重,去重的标准是元素对象的hashCode方法和equals方法
java
Stream<Integer> stream = Stream.of(2, 2, 3, 7, 8);
// 去重
Stream<Integer> stream2 = stream.distinct();
stream2.forEach((x) -> {
System.out.print(x + "\t");
});
//2 3 7 8
映射 map(Function<? super T, ? extends R> mapper)
可以看到接收的是一个函数参数,这个函数会计算每个元素,然后映射成一个新的元素。比如 People对象 取其中一个字段name,可以将List 映射成 List
java
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
List<String> collect = people.stream().map(PredicateDemo.People::getName).collect(Collectors.toList());
映射 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
简单的理解就是可以将多个流转成一个流,可以看到Function中第二个参数就是Stream。
java
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
List<PredicateDemo.People> people2 = new PredicateDemo().mockPeopleList();
List<PredicateDemo.People> people3 = new PredicateDemo().mockPeopleList();
List<List<PredicateDemo.People>> listOfLists = ListUtil.of(people, people2, people3);
List<PredicateDemo.People> peopleTogether = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
排序 sorted(Comparator<? super T> comparator)
java
Stream<Integer> stream = Stream.of(2, 1, 7, 3, 8);
// 自定义排序方式,将元素从大到小排序
Stream<Integer> stream2 = stream.sorted((a, b) -> {
return Math.negateExact(a - b);
});
stream2.forEach((x) -> {
System.out.print(x + "\t");
});
// 8 7 3 2 1
消费 peek(Consumer<? super T> action)
peek接收的是一个Consumer函数,Consumer前面介绍过了,Consumer是消费者,是接收参数但是没有返回值,这里看下peek是怎么使用的Consumer。
java
@Test
public void peek(){
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
people.stream()
.peek(x -> System.out.println("before map: " + x.getName()))
.collect(Collectors.toList());
List<PredicateDemo.People> collect = people.stream()
.peek(x -> x.setName(RandomUtil.randomString(5)))
.collect(Collectors.toList());
}
Stream流终端操作
全匹配 allMatch(Predicate<? super T> predicate)
通过Predicate函数去判断是否符合条件,然后每个元素否符合返回true,否则返回false。
java
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
// 判断stream中是否全是偶数
boolean allMatch = stream.allMatch(x -> x > 1);
System.out.println(allMatch); // true
除此之外还有以下几种匹配操作:
- noneMatch
- anyMatch
查找 findFirst()
返回流中第一个元素
java
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
// 返回流中第一个元素
Optional<Integer> findFirst = stream.findFirst();
System.out.println(findFirst.get());// 2
其他的还有:
- findAny 返回流中任意一个元素
统计 count()
统计元素个数总数
java
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
// 返回流中元素的总个数
long count = stream.count();
System.out.println(count);// 5
最大值 max()
返回流中最大值
java
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
// 返回流中元素最大值
Integer max = stream.max(Integer::compare).get();
System.out.println(max);// 7
与之相应的还有
- min() 返回最小值
归并 reduce(BinaryOperator accumulator)
第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推将元素故并计算得出结果。这个过程类似大数据map-reduce的过程,先分散再聚合。
java
Stream<Integer> stream = Stream.of(2, 4, 5, 6, 7);
// 实现数字累加
Optional<Integer> reduce = stream.reduce((a, b) -> {
return a + b;
});
System.out.println(reduce.get()); // 24
收集 collect
集合 toList / toSet / toMap
java
@Test
public void map(){
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
List<PredicateDemo.People> collect1 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toList());
Set<PredicateDemo.People> collect2 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toSet());
Map<Integer, PredicateDemo.People> collect3 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.toMap(p -> p.getId(), p -> p));
}
平均值 averagingInt()
java
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
Double av = stream.collect(Collectors.averagingInt((x) -> {
return x;
}));
System.out.println(av); // 2.4
求和 summingInt()
java
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
Integer sum = stream.collect(Collectors.summingInt((x) -> {
return x;
}));
System.out.println(sum); // 12
最大值 maxBy()
java
Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4);
Optional<Integer> max = stream.collect(Collectors.maxBy(Integer::compare));
System.out.println(max.get()); // 4
分组 groupingBy()
将元素进行分组。
java
@Test
public void groupingBy(){
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
Map<String, List<PredicateDemo.People>> collect = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.groupingBy(p -> p.getSex()));
}
分区 partitioningBy(Predicate<? super T> predicate)
参数是Predicate,所以就是将流按照是否符合条件,分成两部分。
java
@Test
public void partitioningBy(){
List<PredicateDemo.People> people = new PredicateDemo().mockPeopleList();
Map<Boolean, List<PredicateDemo.People>> collect1 = people.stream().filter(p -> p.getAge() > 18).collect(Collectors.partitioningBy(p -> p.getLevel() > 5));
}
总结
在Stream中经常遇到的filter、map、collect等操作方法的概念以及含义是什么,该如何使用,都通过案例和使用场景做了一一讲解。还拓展了其他的一些常见的方法,结合下面这几篇系列的文章,对函数有个熟悉的掌握,然后再使用Stream时,遇到的一些方法,该传递什么函数参数,以及返回结果是什么,就了然于胸了。