StreamAPI
Java8API中添加了新的抽象称为流Stream,把真正的函数式编程风格引入到Java中,可以让你以一种声明的方式处理数据,Stream使用一种类似SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象,StreamAPI极大简化了集合框架的处理,这种风格将需要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如:筛选、排序、聚合。
Stream流有一些新特性:
- Stream流不是一种数据类型,不保存数据,他只是在原数据集上定义了一组操作
- 这些操作是惰性的,即每当访问到这个流中的一个元素时,才会在此元素上执行这一系列的操作
- Stream流不保存数据,所以一个Stream流只能使用一次
- 元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果:
- 中间操作:返回结果都是Stream,所以中间操作可以叠加
- 最终操作:返回我们最终需要的数据,只能有一个最终操作
使用Stream流,可以清楚地知道我们要对一个数据集做何操作,可读性强。而且可以很轻松地获取并行化Stream流,不用自己编写多线程代码,可以更加专注于业务逻辑。默认情况下,从有序集合、生成器、迭代器产生的流或者通过调用Stream.sorted产生的流都是有序流,有序流在并行处理时会在处理完成后恢复原顺序。无限流的存在侧面说明了流是惰性的,即每当用到一个元素时,才会在这个元素上执行这一系列操作。
使用Stream流的基本操作:
- 创建Stream
- 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以进行多次转换)
- 对Stream进行聚合操作,获得想要的结果
流的创建
通过集合创建
Java8中Collection接口被扩展,提供了两个获取流的方法:
- default Stream stream();返回一个顺序流
- default Stream parallelStream();返回一个并行流
通过数组创建
Java8中的Arrays的静态方法stream()可以获取数组流
- public staticStream staram(T[] array);返回一个流
- public static IntStream stream(int[] array);返回一个整型的数据流
- public static LongStream stream(long[] array);返回一个长整形的数据流
- public static DoubleStream stream(double[] array);返回一个浮点型数据流
通过Stream创建
1、可以调用Stream类静态方法of(),通过显示值创建一个流,它可以接收任务数量的参数。
- public static Stream of(T …values);返回一个顺序流
2、静态方法concat(),将两个流连接起来
- public static Stream concat(Stream<? extends T> a, Stream<? extends T> b);
创建无限流
可以使用Stream中的静态方法Stream.iterate()和Stream.generate(),创建一个无限流
- public static Stream iterate(final T seed,final UnaryOperatorf);返回一个无限流
- public static Stream generate(Supplier s);返回一个无限流
中间操作
返回值 | 方法名 | 说明 |
---|---|---|
Stream | filter(Predicate p) | 筛选,接收Lambda,从流中排除某些元素 |
Stream | distinct() | 筛选,通过流所生成元素的equals()去除重复元素 |
Stream | limit(long maxSize) | 截断,使流中元素不超过指定maxSize |
Stream | skip(long n) | 切片,跳过元素,返回一个扔掉了n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 |
Stream | peek(Consumer action) | 消费,接收Lambda,对流中每一个数据执行Lambda体操作 |
Stream | sorted() | 排序,产生一个新流,其中按自然顺序排序 |
Stream | sorted(Comparator com) | 排序,产生一个新流,其中按给定比较器顺序排序 |
Stream | map(Function f) | 映射,接收一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素 |
Stream | flatMap(Function f) | 映射,接收一个函数作为参数,将流中的每一个值都替换为另一个流,然后把所有流连接成一个流 |
DoubleStream | mapToDouble(ToDoubleFunction f) | 映射,接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的DoubleStream |
IntStream | mapToInt(ToIntFunction f) | 映射,接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的IntStream |
LongStream | mapToLong(ToLongFunction f) | 映射,接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的LongStream |
中间操作:筛选和切片
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1001,"张三",3000));
list.add(new Employee(1002,"李四",5500));
list.add(new Employee(1003,"王五",8000));
list.add(new Employee(1004,"赵六",6000));
list.add(new Employee(1004,"赵六",6000));
list.add(new Employee(1004,"赵六",6000));
list.stream()
.filter((x)->x.getSalary()>5000)//过滤掉工资小于5000的元素
.limit(4)//只留下前四个元素
.skip(1)//去掉第一个元素
.distinct()//去掉重复元素,需要自定义类Employee重写equals和hashCode方法
.forEach(System.out::println);
中间操作:消费
List<String> list2 = Arrays.asList("a","bb","c","d");
list2.stream()
.peek(System.out::println)//消费这个流中的元素,打印每一个元素
//注意,这是中间操作,所以需要在下面增加一个终止操作,上面的中间操作才会执行
.collect(Collectors.toList());
中间操作:排序
List<String> list1 = Arrays.asList("dd","b","aaa","cccc");
list1.stream()
.sorted()//自然排序,使用String类中的compareTo方法实现排序(Comparable)
.forEach(System.out::println);
System.out.println("-----------------------------------------");
list1.stream()
.sorted((a,b)-> {
if (a.length() > b.length()){
return 1;
}else {
return -1;
}
})//定制排序,按字符串长度排序
.forEach(System.out::println);
中间操作:映射
String[] strs = {"java","lambda","stream","map","flatmap"};
Arrays.stream(strs)
.map((str)->str.split(""))//将每一个字符串都转换为一个字符串数组流
.forEach(System.out::println);//打印的字符串数组
Arrays.stream(strs)
.map((str)->str.split(""))
.flatMap(Arrays::stream)//将得到的每一个字符串数组流扁平化,在构成一个总流
.forEach(System.out::println);//打印总流中的每一个字符
终结操作
终结操作是会从流的流水线生成结果,其结果可以是任何不是流的值,如List、Integer、void也可以。流进行了终止操作后,就不能再次使用。
返回值 | 方法名 | 说明 |
---|---|---|
boolean | allMatch(Predicate p) | 接收一个Predicate函数,当流中每个元素都符合该断言时,才会返回true,否则返回false |
boolean | noneMatch(Predicate p) | 接收一个Predicate函数,当流中每一个元素都不符合该断言时才返回true,否则返回false |
boolean | anyMatch(Predicate p) | 接收一个Predicate函数,当流中有一个元素满足该断言则返回true,否则返回false |
Optional | findFirst() | 返回流中的第一个元素 |
Optional | findAny() | 返回流中的任意一个元素 |
long | count() | 返回流中元素的总个数 |
Optional | max(Comparator c) | 返回流中元素的最大值 |
Optional | min(Comparator c) | 返回流中元素的最小值 |
void | forEach(Consumer c) | 遍历操作,对此流的每个元素执行操作 |
T | reduce(T iden,BinaryOperator b) | 规约操作,使用提供的身份值和 associative累积函数对此流的元素执行reduction,并返回减小的值。 |
U | reduce(BinaryOperator b) | 规约操作,使用提供的身份,积累和组合功能,对此流的元素执行reduction |
R | collect(Collector c) | 收集操作,收集一个Collector实例,将流中元素收集成另一个数据结构 |
终结操作:查找
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1001,"张三",3000));
list.add(new Employee(1002,"李四",5500));
list.add(new Employee(1003,"王五",8000));
list.add(new Employee(1004,"赵六",6000));
boolean b1 = list.stream()
.allMatch(e -> e.getSalary()>3000);
System.out.println("员工工资是否都大于3000:"+b1);
boolean b2 = list.stream()
.anyMatch(e -> e.getSalary()>7000);
System.out.println("员工工资是否有大于7000的:"+b2);
boolean b3 = list.stream()
.noneMatch(e -> e.getSalary()>10000);
System.out.println("员工工资是否都不大于一万:"+b3);
//因为得到的值有可能为空,所以返回的对象是一个Optional
Optional<Employee> op = list.stream()
.sorted(Comparator.comparingDouble(Employee::getSalary))
.findFirst();
System.out.println("工资最低的员工:"+op.get());
//随机返回一个工资大于五千的员工信息
Optional<Employee> op1 = list.parallelStream()
.filter(e->e.getSalary()>5000)
.findAny();
System.out.println("工资大于5000的任意一个员工:"+op1.get());
终结操作:匹配
long count = list.stream()
.count();
System.out.println("员工总数为:"+count);
//返回流中的最大值
Optional<Employee> op3 = list.stream()
.max(Comparator.comparingDouble(Employee::getSalary));
System.out.println("工资最高的员工:"+op3.get());
//返回流中的最小值
Optional<Double> op4 = list.stream()
.map(Employee::getSalary)
.min(Double::compare);
System.out.println("最低工资为:"+op4.get());