Stream API 笔记
Stream是一组用来处理数组,集合的API。
1.1 特性
不是数据结构,没有内部存储。
不支持索引访问。
延迟计算
支持并行
很容易生成数据或集合
支持过滤,查找,转换,汇总,聚合等操作。
1.2 运行机制
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
Stream只有遇到终止操作,它的源才会开始执行遍历操作。
1.3 Stream的创建
通过数组,Stream.of()
通过集合
通过Stream.generate方法来创建
通过Stram.iterate方法
其他API
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class CreateStream {
//通过数组,Stream.of()
static void gen1(){
String[] str = {"a","b","c"};
Stream<String> str1 = Stream.of(str);
}
//通过集合
static void gen2(){
List<String> strings = Arrays.asList("a", "b", "c");
Stream<String> stream = strings.stream();
}
//通过Stream.generate方法来创建
static void gen3(){
//这是一个无限流,通过这种方法创建在操作的时候最好加上limit进行限制
Stream<Integer> generate = Stream.generate(() -> 1);
generate.limit(10).forEach(x -> System.out.println(x));
}
//通过Stram.iterate方法
static void gen4(){
Stream<Integer> iterate = Stream.iterate(1, x -> x +1);
iterate.forEach(x -> System.out.println(x));
}
//其他API
static void gen5(){
String str = "abc";
IntStream chars = str.chars();
chars.forEach(x -> System.out.println(x));
}
}
2 Stream常用的API
2.1 中间操作
2.1.1 filter 过滤
该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。说白了就是给一个条件,filter会根据这个条件截取流中得数据。
public static void testFilter(){
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//截取所有能被2整除得数据
List<Integer> collect = integers.stream().filter(i -> i % 2 == 0).collect(Collectors.toList());
System.out.println("collect = " + collect);
}
结果:
collect = [2, 4, 6, 8, 10]
2.1.2 distinct 去重
该操作会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。注意:必须重写对应泛型的hashCode()和equals()方法
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
List<Integer> collect = numbers.stream().distinct().collect(Collectors.toList());
System.out.println("collect = " + collect);
}
1
2
3
4
5
结果:
collect = [1, 2, 3, 4]
2.1.3 sorted 排序
对流中得数据进行排序,可以以自然序或着用Comparator 接口定义的排序规则来排序一个流。Comparator 能使用lambada表达式来初始化,还能够逆序一个已经排序的流。
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(5, 8, 2, 6, 41, 11);
//排序默认为顺序 顺序 = [2, 5, 6, 8, 11, 41]
List<Integer> sorted = integers.stream().sorted().collect(Collectors.toList());
System.out.println("顺序 = " + sorted);
//逆序 逆序 = [41, 11, 8, 6, 5, 2]
//Comparator.reverseOrder() 将排序结果翻转
List<Integer> reverseOrder = integers.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println("逆序 = " + reverseOrder);
//也可以接收一个lambda
List<Integer> ages = integers.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
//如果是根据实体字段进行排序
List<User> users= Arrays.asList();
List<User> list = users.stream()
.filter(e -> e.getSort() != null)
.sorted(Comparator.comparing(User::getSort).collect(Collectors.toList());
//如果是根据多个实体字段进行排序
list = users.stream()
.filter(e -> e.getSort() != null)
.sorted(Comparator.comparing(User::getSort)
.thenComparing(User::getId,Comparator.reverseOrder())).collect(Collectors.toList());
}
2.1.4 limit 截取
该方法会返回一个不超过给定长度的流。
public static void testLimit(){
List<Integer> integers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
//截取流中得前三个元素 collect = [1, 2, 1]
List<Integer> collect = integers.stream().limit(3).collect(Collectors.toList());
System.out.println("collect = " + collect);
}
2.1.5 skip 舍弃
该方法会返回一个扔掉了前面n个元素的流。如果流中元素不足n个,则返回一个空流。
public static void testSkip(){
List<Integer> integers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
//丢掉流中得前三个元素 collect = [3, 3, 2, 4]
List<Integer> collect = integers.stream().skip(3).collect(Collectors.toList());
System.out.println("collect = " + collect);
}
2.1.6 map 归纳
该方法会接受一个函数作为参数,这个函数会被应用到每个元素上,并将其映射成一个新的元素。就是根据指定函数获取流中得每个元素得数据并重新组合成一个新的元素。
public static void main(String[] args) {
//自己建好得一个获取对象list得方法
List<Dish> dishList = Dish.getDishList();
//获取每一道菜得名称 并放到一个list中
List<String> collect = dishList.stream().map(Dish::getName).collect(Collectors.toList());
//collect = [pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon]
System.out.println("collect = " + collect);
}
2.1.7 flatMap 扁平化
该方法key可以让你把一个流中的每个值都换成另一个流,然后把所有的流都链接起来成为一个流。
给 定 单 词 列 表[“Hello”,“World”] ,你想要返回列表 [“H”,“e”,“l”, “o”,“W”,“r”,“d”],你可能会认为这很容易,通过map你可以把每个单词映射成一张字符表,然后调用 distinct 来过滤重复的字符,但是这个方法的问题在于,传递给 map 方法的Lambda为每个单词返回了一个 String[] ( String列表)。因此, map 返回的流实际上是Stream<String[]> 类型的。而你真正想要的是用Stream 来表示一个字符流。
正确写法应该是通过flatMap对其扁平化并作出对应处理。
public static void main(String[] args) {
String[] words = {"Hello", "World"};
List<String> collect = Stream.of(words). //数组转换流
map(w -> w.split("")). //去掉“”并获取到两个String[]
flatMap(Arrays::stream). //方法调用将两个String[]扁平化为一个stream
distinct(). //去重
collect(Collectors.toList());
//collect = [H, e, l, o, W, r, d]
System.out.println("collect = " + collect);
}
}
2.1.8 peek
peek 的设计初衷就是在流的每个元素恢复运行之前,插入执行一个动作。
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(2, 3, 4, 5);
List<Integer> result =
numbers.stream()
.peek(x -> System.out.println("from stream: " + x))
.map(x -> x + 17)
.peek(x -> System.out.println("after map: " + x))
.filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter: " + x))
.limit(3)
.peek(x -> System.out.println("after limit: " + x))
.collect(Collectors.toList());
}
结果:
from stream: 2
after map: 19
from stream: 3
after map: 20
after filter: 20
after limit: 20
from stream: 4
after map: 21
from stream: 5
after map: 22
after filter: 22
after limit: 22
2.1.9 collect 收集
从上面得代码已经可以看出来,collect是将最终stream中得数据收集起来,最终生成一个list,set,或者map。
public static void main(String[] args) {
List<Dish> dishList = Dish.getDishList();
//list
List<Dish> collect = dishList.stream().limit(2).collect(Collectors.toList());
//set
Set<Dish> collect1 = dishList.stream().limit(2).collect(Collectors.toSet());
//map
Map<String, Dish.Type> collect2 = dishList.stream().limit(2).collect(Collectors.toMap(Dish::getName, Dish::getType));
}
这里面生成map得toMap方法有三个重载,传入得参数都不同,这里使用得是传入两个Function类型得参数。当然,Collectors的功能还不止这些,下面的收集器中会有其他的详解。
2.2 终止操作
循环 forEach
计算 min、max、count、average
匹配 anyMatch、allMatch、noneMatch、findFirst、findAny
汇聚 reduce
收集器 collect
2.3 查找和匹配
另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream API通过allMatch,anyMatch,noneMatch,findFirst和findAny方法提供了这样的工具。
查找和匹配都是终端操作。
2.3.1 anyMatch
anyMatch方法可以回答“流中是否有一个元素能匹配到给定的谓词”。会返回一个boolean值。
public class AnyMatch {
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
boolean b = dish.stream().anyMatch(Dish::isVegetarian);
System.out.println(b);
}
}
2.3.2 allMatch
allMatch方法和anyMatch类似,校验流中是否都能匹配到给定的谓词。
class AllMatch{
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//是否所有菜的热量都小于1000
boolean b = dish.stream().allMatch(d -> d.getCalories() < 1000);
System.out.println(b);
}
}
2.3.3 noneMatch
noneMatch方法可以确保流中没有任何元素与给定的谓词匹配。
class NoneMatch{
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//没有任何菜的热量大于等于1000
boolean b = dish.stream().allMatch(d -> d.getCalories() >= 1000);
System.out.println(b);
}
}
anyMatch,noneMatch,allMatch这三个操作都用到了所谓的短路。
2.3.4 findAny
findAny方法将返回当前流中的任意元素。
class FindAny{
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
Optional<Dish> any = dish.stream().filter(Dish::isVegetarian).findAny();
System.out.println("any = " + any);
}
}
2.3.5 findFirst
findFirst方法能找到你想要的第一个元素。
class FindFirst{
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
Optional<Dish> any = dish.stream().filter(Dish::isVegetarian).findFirst();
System.out.println("any = " + any);
}
}
2.4 归约 reduce
此类查询需要将流中所有元素反复结合起来,得到一个值,比如一个 Integer 。这样的查询可以被归类为归约操作(将流归约成一个值)。用函数式编程语言的术语来说,这称为折叠(fold),因为你可以将这个操
作看成把一张长长的纸(你的流)反复折叠成一个小方块,而这就是折叠操作的结果。
2.4.1 元素求和
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3, 6, 8);
//求list中的和,以0为基数
Integer reduce = integers.stream().reduce(0, (a, b) -> a + b);
//Integer的静态方法
int sum = integers.stream().reduce(0, Integer::sum);
System.out.println("reduce = " + reduce);
}
2.4.2 最大值和最小值
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3, 6, 8);
Optional<Integer> min = integers.stream().reduce(Integer::min);
System.out.println("min = " + min);
Optional<Integer> max = integers.stream().reduce(Integer::max);
System.out.println("max = " + max);
}
2.5 收集器 Collectors
2.5.1 查找流中的最大值和最小值 minBy maxBy
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//创建一个Comparator来进行比较 比较菜的卡路里
Comparator<Dish> dishComparator = Comparator.comparingInt(Dish::getCalories);
//maxBy选出最大值
Optional<Dish> collect = dish.stream().collect(Collectors.maxBy(dishComparator));
System.out.println("collect = " + collect);
//选出最小值
Optional<Dish> collect1 = dish.stream().collect(Collectors.minBy(dishComparator));
System.out.println("collect1 = " + collect1);
}
2.5.2 汇总 summingInt
Collectors.summingInt 。它可接受一个把对象映射为求和所需 int 的函数,并返回一个收集器。
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//计算总和
int collect = dish.stream().collect(Collectors.summingInt(Dish::getCalories));
System.out.println("collect = " + collect);
}
2.5.3 平均数 averagingInt
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//计算平均数
Double collect = dish.stream().collect(Collectors.averagingInt(Dish::getCalories));
System.out.println("collect = " + collect);
}
2.5.4 连接字符串 joining
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
String collect = dish.stream().map(Dish::getName).collect(Collectors.joining());
System.out.println("collect = " + collect);
}
joining 工厂方法有一个重载版本可以接受元素之间的分界符,这样你就可以得到一个逗号分隔的菜肴名称列表。
String collect = dish.stream().map(Dish::getName).collect(Collectors.joining(", "));
1
2.5.5 得到流中的总数 counting
long howManyDishes = dish.stream().collect(Collectors.counting());
1
2.6 分组
2.6.1 分组 groupingBy
public static void main(String[] args) {
List<Dish> dish = Dish.getDish();
//groupingBy接受一个function作为参数
Map<Dish.Type, List<Dish>> collect = dish.stream().collect(Collectors.groupingBy(Dish::getType));
System.out.println("collect = " + collect);
}
————————————————
版权声明:本文为CSDN博主「爱思考的王同学」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37967166/article/details/89153568