【Java8新特性】面试官:谈谈Java8中的Stream API有哪些终止操作?
写在前面
如果你出去面试,面试官问了你关于Java8 Stream API的一些问题,比如:Java8中创建Stream流有哪几种方式?(可以参见:《【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?》)Java8中的Stream API有哪些中间操作?(可以参见:《【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!》)如果你都很好的回答了这些问题,那么,面试官可能又会问你:Java8中的Stream API有哪些终止操作呢?没错,这就是Java8中有关Stream API的灵魂三问!不要觉得是面试官在为难你,只有你掌握了这些细节,你就可以反过来吊打面试官了!
Stream的终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer、Double、String等等,甚至是 void 。
在Java8中,Stream的终止操作可以分为:查找与匹配、规约和收集。接下来,我们就分别简单说明下这些终止操作。
查找与匹配
Stream API中有关查找与匹配的方法如下表所示。
| 方法 | 描述 |
|---|---|
| allMatch(Predicate p) | 检查是否匹配所有元素 |
| anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
| noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
| findFirst() | 返回第一个元素 |
| findAny() | 返回当前流中的任意元素 |
| count() | 返回流中元素总数 |
| max(Comparator c) | 返回流中最大值 |
| min(Comparator c) | 返回流中最小值 |
| forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代) |
同样的,我们对每个重要的方法进行简单的示例说明,这里,我们首先建立一个Employee类,Employee类的定义如下所示。
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = -9079722457749166858L;
private String name;
private Integer age;
private Double salary;
private Stauts stauts;
public enum Stauts{
WORKING,
SLEEPING,
VOCATION
}
}
接下来,我们在测试类中定义一个用于测试的集合employees,如下所示。
protected List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 9999.99, Employee.Stauts.SLEEPING),
new Employee("李四", 38, 5555.55, Employee.Stauts.WORKING),
new Employee("王五", 60, 6666.66, Employee.Stauts.WORKING),
new Employee("赵六", 8, 7777.77, Employee.Stauts.SLEEPING),
new Employee("田七", 58, 3333.33, Employee.Stauts.VOCATION)
);
好了,准备工作就绪了。接下来,我们就开始测试Stream的每个终止方法。
1.allMatch()
allMatch()方法表示检查是否匹配所有元素。其在Stream接口中的定义如下所示。
boolean allMatch(Predicate<? super T> predicate);
我们可以通过类似如下示例来使用allMatch()方法。
boolean match = employees.stream().allMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用allMatch()方法时,只有所有的元素都匹配条件时,allMatch()方法才会返回true。
2.anyMatch()方法
anyMatch方法表示检查是否至少匹配一个元素。其在Stream接口中的定义如下所示。
boolean anyMatch(Predicate<? super T> predicate);
我们可以通过类似如下示例来使用anyMatch()方法。
boolean match = employees.stream().anyMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用anyMatch()方法时,只要有任意一个元素符合条件,anyMatch()方法就会返回true。
3.noneMatch()方法
noneMatch()方法表示检查是否没有匹配所有元素。其在Stream接口中的定义如下所示。
boolean noneMatch(Predicate<? super T> predicate);
我们可以通过类似如下示例来使用noneMatch()方法。
boolean match = employees.stream().noneMatch((e) -> Employee.Stauts.SLEEPING.equals(e.getStauts()));
System.out.println(match);
注意:使用noneMatch()方法时,只有所有的元素都不符合条件时,noneMatch()方法才会返回true。
4.findFirst()方法
findFirst()方法表示返回第一个元素。其在Stream接口中的定义如下所示。
Optional<T> findFirst();
我们可以通过类似如下示例来使用findFirst()方法。
Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
System.out.println(op.get());
5.findAny()方法
findAny()方法表示返回当前流中的任意元素。其在Stream接口中的定义如下所示。
Optional<T> findAny();
我们可以通过类似如下示例来使用findAny()方法。
Optional<Employee> op = employees.stream().filter((e) -> Employee.Stauts.WORKING.equals(e.getStauts())).findFirst();
System.out.println(op.get());
6.count()方法
count()方法表示返回流中元素总数。其在Stream接口中的定义如下所示。
long count();
我们可以通过类似如下示例来使用count()方法。
long count = employees.stream().count();
System.out.println(count);
7.max()方法
max()方法表示返回流中最大值。其在Stream接口中的定义如下所示。
Optional<T> max(Comparator<? super T> comparator);
我们可以通过类似如下示例来使用max()方法。
Optional<Employee> op = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op.get());
8.min()方法
min()方法表示返回流中最小值。其在Stream接口中的定义如下所示。
Optional<T> min(Comparator<? super T> comparator);
我们可以通过类似如下示例来使用min()方法。
Optional<Double> op = employees.stream().map(Employee::getSalary).min(Double::compare);
System.out.println(op.get());
9.forEach()方法
forEach()方法表示内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代)。其在Stream接口内部的定义如下所示。
void forEach(Consumer<? super T> action);
我们可以通过类似如下示例来使用forEach()方法。
employees.stream().forEach(System.out::println);
规约
Stream API中有关规约的方法如下表所示。
| 方法 | 描述 |
|---|---|
| reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 T |
| reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 Optional |
reduce()方法在Stream接口中的定义如下所示。
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
我们可以通过类似如下示例来使用reduce方法。
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(op.get());
我们也可以搜索employees列表中“张”出现的次数。
Optional<Integer> sum = employees.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六'))
return 1;
else
return 0;
}).reduce(Integer::sum);
System.out.println(sum.get());
注意:上述例子使用了硬编码的方式来累加某个具体值,大家在实际工作中再优化代码。
收集
| 方法 | 描述 |
|---|---|
| collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
collect()方法在Stream接口中的定义如下所示。
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
我们可以通过类似如下示例来使用collect方法。
Optional<Double> max = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = employees.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = employees.stream().collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
如何收集Stream流?
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到 List、 Set、 Map)。 Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例, 具体方法与实例如下表:
| 方法 | 返回类型 | 作用 |
|---|---|---|
| toList | List | 把流中元素收集到List |
| toSet | Set | 把流中元素收集到Set |
| toCollection | Collection | 把流中元素收集到创建的集合 |
| counting | Long | 计算流中元素的个数 |
| summingInt | Integer | 对流中元素的整数属性求和 |
| averagingInt | Double | 计算流中元素Integer属性的平均 值 |
| summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。 如:平均值 |
| joining | String | 连接流中每个字符串 |
| maxBy | Optional | 根据比较器选择最大值 |
| minBy | Optional | 根据比较器选择最小值 |
| reducing | 归约产生的类型 | 从一个作为累加器的初始值 开始,利用BinaryOperator与 流中元素逐个结合,从而归 约成单个值 |
| collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结 果转换函数 |
| groupingBy | Map<K, List> | 根据某属性值对流分组,属 性为K,结果为V |
| partitioningBy | Map<Boolean, List> | 根据true或false进行分区 |
每个方法对应的使用示例如下表所示。
| 方法 | 使用示例 |
|---|---|
| toList | List employees= list.stream().collect(Collectors.toList()); |
| toSet | Set employees= list.stream().collect(Collectors.toSet()); |
| toCollection | Collection employees=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
| counting | long count = list.stream().collect(Collectors.counting()); |
| summingInt | int total=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
| averagingInt | double avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)) |
| summarizingInt | IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
| Collectors | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
| maxBy | Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
| minBy | Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
| reducing | int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
| collectingAndThen | int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
| groupingBy | Map<Emp.Status, List> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); |
| partitioningBy | Map<Boolean,List>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collecors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
写在最后
如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Java8新特性。
最后,附上Java8新特性核心知识图,祝大家在学习Java8新特性时少走弯路。

【Java8新特性】面试官:谈谈Java8中的Stream API有哪些终止操作?的更多相关文章
- 【Java8新特性】你知道Java8为什么要引入Lambda表达式吗?
写在前面 这是一道真实的面试题,一个读者朋友出去面试,面试官竟然问他这样一个问题:你说说Java8中为什么引入Lambda表达式?引入Lambda表达式后有哪些好处呢?还好这个朋友对Java8早有准备 ...
- 2020你还不会Java8新特性?方法引用详解及Stream 流介绍和操作方式详解(三)
方法引用详解 方法引用: method reference 方法引用实际上是Lambda表达式的一种语法糖 我们可以将方法引用看作是一个「函数指针」,function pointer 方法引用共分为4 ...
- java8新特性lamda表达式在集合中的使用
1.利用stream().forEach()循环处理List; List<String> list = Lists.newArrayList();//新建一个List 用的google提供 ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- 2020你还不会Java8新特性?
Java8(1)新特性介绍及Lambda表达式 前言: 跟大娃一块看,把原来的电脑拿出来放中间看视频用 --- 以后会有的课程 难度 深入Java 8 难度1 并发与netty 难度3 JVM 难度4 ...
- Java8新特性(一)之Lambda表达式
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?
写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...
- 【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!
写在前面 在上一篇<[Java8新特性]面试官问我:Java8中创建Stream流有哪几种方式?>中,一名读者去面试被面试官暴虐!归根结底,那哥儿们还是对Java8的新特性不是很了解呀!那 ...
- Java8新特性(一)_interface中的static方法和default方法
什么要单独写个Java8新特性,一个原因是我目前所在的公司用的是jdk8,并且框架中用了大量的Java8的新特性,如上篇文章写到的stream方法进行过滤map集合.stream方法就是接口Colle ...
随机推荐
- CodeForces - 1047A
A. Little C Loves 3 I time limit per test1 second memory limit per test256 megabytes inputstandard i ...
- 图论--2-SAT--详解
问题描述: 现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x]AND A[y]=0.A[x] OR A[y] OR A[z]=1等,要确定A[0..N-1]的值,使得其满足所有限制关系. ...
- C++ 模板(template) 的定义
定义: 模板(template)是实现代码重用机制的一种工具,它可以实现类型参数化,把类型定义为参数(模板元编程),从而实现了真正的代码可重用性. 模板是用来批量生成功能和形式都几乎相同的代码的.编译 ...
- python(递归函数)
1.描述: 递归函数:在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. 2.递归函数特性: 必须有一个明确的结束条件: 每次进入更深一层递归时,问题规模相比上次递归 ...
- muduo网络库源码学习————无界队列和有界队列
muduo库里实现了两个队列模板类:无界队列为BlockingQueue.h,有界队列为BoundedBlockingQueue.h,两个测试程序实现了生产者和消费者模型.(这里以无界队列为例,有界队 ...
- three.js中的矩阵变换(模型视图投影变换)
目录 1. 概述 2. 基本变换 2.1. 矩阵运算 2.2. 模型变换矩阵 2.2.1. 平移矩阵 2.2.2. 旋转矩阵 2.2.2.1. 绕X轴旋转矩阵 2.2.2.2. 绕Y轴旋转矩阵 2.2 ...
- redis关闭报没有权限No auth
Redis安装之后,如果设置了密码,需要在关闭服务的时候也提供密码,否则无法关闭服务,可以直接通过修改/etc/init.d/redis启动脚本解决. Redis服务的启动和关闭脚本,只需要在关闭的脚 ...
- docker 生产环境基础应用
项目背景 此项目是在已有项目基础上,开发的一套相对独立的系统.项目总体分为三部分,前端.中间应用服务.流媒体服务.前端技术选型为vue+elementui,中间应用服务为.net core webap ...
- Bootstrap初识
目录 概述 快速入门 响应式布局 CSS样式和JS插件 全局CSS样式 组件 插件 案例:黑马旅游网 概述 概念:一个前端开发的框架,Bootstrap是美国Twitter公司的设计师Mark Ott ...
- C++中的隐式转换和explicit
隐式转换 c++中的数据类型转换分为隐式转换和显示转换: 显示转换即使用static_cast等方法进行转换,相关内容请参考 <C++数据类型转换>: 隐式转换则是编译器完成的,如,boo ...