开发记录:关于Java Stream,涉及遍历、分组以及list转map、list字段提取
开发记录:关于Java Stream,涉及遍历、分组以及list转map、list字段提取
简介和特点
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
上述流程转换为Java代码为:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
什么是Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
常见的方法
-
forEach():迭代流中的每个数据
-
map():映射每个元素到对应的结果
-
Collectors类:实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串。通常在 .collect(Collectors.方法)
-
filter():设置的条件过滤出元素
案例
案例一:根据医院编号,查询医院所有科室列表(并封装)
collect分组
- 实体类:
DepartmentVo
@Data
@ApiModel(description = "Department")
public class DepartmentVo {
@ApiModelProperty(value = "科室编号")
private String depcode;
@ApiModelProperty(value = "科室名称")
private String depname;
@ApiModelProperty(value = "下级节点")
private List<DepartmentVo> children;
}
Department
@Data
@ApiModel(description = "Department")
@Document("Department")
public class Department extends BaseMongoEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "医院编号")
@Indexed //普通索引
private String hoscode;
@ApiModelProperty(value = "科室编号")
@Indexed(unique = true) //唯一索引
private String depcode;
@ApiModelProperty(value = "科室名称")
private String depname;
@ApiModelProperty(value = "科室描述")
private String intro;
@ApiModelProperty(value = "大科室编号")
private String bigcode;
@ApiModelProperty(value = "大科室名称")
private String bigname;
}
-
stream流的功能代码
List
departmentList:某指定医院的所有科室列表
使用collect方法,对departmentList中所有Department对象进行分组,按照大科室的code分组,即Department中的bigcode字段。相同bigcode分在一个list中,key则是bigcode
//根据大科室编号 bigcode 分组,每个value是一个大科室list,其中成员是所对应的小科室对象
Map<String, List<Department>> deparmentMap = departmentList.stream().collect(Collectors.groupingBy(Department::getBigcode));
返回的数据,一部分:
{
a4e171f4cf9b6816acdfb9ae62c414d7=[Department(hoscode=1000_0, depcode=200040878, depname=多发性硬化专科门诊, intro=多发性硬化专科门诊, bigcode=a4e171f ...), Department(...), Department(...) ....], 0551a547cc19d3d09f2e57bd2931b7d0=[Department ......], ...
}
- 完整代码
//根据医院编号,查询医院所有科室列表(并封装)
@Override
public List<DepartmentVo> findDeptTree(String hoscode) {
//###########################################################
//创建list集合,用于最终数据封装
List<DepartmentVo> result = new ArrayList<>(); // DepartmentVo实体类,与数据库Department表建立映射关系
//根据医院编号,查询医院所有科室信息
Department departmentQuery = new Department();
departmentQuery.setHoscode(hoscode); // 设置科室表中的医院编号
Example example = Example.of(departmentQuery); // 传入实体
//所有科室列表 departmentList
List<Department> departmentList = departmentRepository.findAll(example); // 得到医院编号字段为指定id的所有科室
//###########################################################
//根据大科室编号 bigcode 分组,每个value是一个大科室list,其中成员是所对应的小科室对象
Map<String, List<Department>> deparmentMap =
departmentList.stream().collect(Collectors.groupingBy(Department::getBigcode));
System.out.println(deparmentMap);
//###########################################################
//遍历map集合 deparmentMap,遍历每一个大科室:对每一个大科室重新进行封装,包含其下级子科室
for(Map.Entry<String,List<Department>> entry : deparmentMap.entrySet()) {
//大科室编号
String bigcode = entry.getKey(); // 即 key
//大科室编号对应的所有下级科室数据,已经建立好bigcode对应下级科室的列表
List<Department> deparment1List = entry.getValue();
//封装大科室
DepartmentVo departmentVo1 = new DepartmentVo();
departmentVo1.setDepcode(bigcode);
departmentVo1.setDepname(deparment1List.get(0).getBigname()); // 每一个下级科室,都有对应的大科室名称,随便选一个子科室获取即可
//封装小科室,将数据库中的科室数据重新封装成DepartmentVo类型,方便之后前端使用
List<DepartmentVo> children = new ArrayList<>();
for(Department department: deparment1List) {
DepartmentVo departmentVo2 = new DepartmentVo();
departmentVo2.setDepcode(department.getDepcode());
departmentVo2.setDepname(department.getDepname());
//封装到list集合
children.add(departmentVo2);
}
//把小科室list集合放到大科室children里面
departmentVo1.setChildren(children);
//放到最终result里面
result.add(departmentVo1);
}
//返回
return result;
}
案例二:获取list集合,使用遍历,重新对集合中的元素进行封装
foreach
// 创建example对象
Example<Hospital> example = Example.of(hospital, matcher);
// 调用方法实现查询,从MongoDB中,获取对象
Page<Hospital> pages = hospitalRepository.findAll(example, pageable);
// 获取查询list集合,遍历进行医院等级封装
pages.getContent().stream().forEach(item -> { // HosType 医院等级,如三甲
this.setHospitalHosType(item); // item 传入的是 Hospital 对象
});
案例三:将List类型转换为Map类型
Collectors.toMap
List<BookingScheduleRuleVo> scheduleVoList = aggregateResult.getMappedResults(); // List
// List转Map,以workdate为key,value是BookingScheduleRuleVo类型的对象
Map<Date, BookingScheduleRuleVo> scheduleVoMap = new HashMap<>();
if(!CollectionUtils.isEmpty(scheduleVoList)) {
scheduleVoMap = scheduleVoList.stream().
collect(
Collectors.toMap(BookingScheduleRuleVo::getWorkDate,
BookingScheduleRuleVo -> BookingScheduleRuleVo));
}
案例四:从List
stream().map().collect(Collectors.toList())
// List<OrderCountVo> orderCountVoList
//获取x需要数据 ,将OrderCountVo中的date过滤,并形成日期列表
List<String> dateList = orderCountVoList.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
//获取y需要数据,过滤OrderCountVo中的count,并形成数量列表
List<Integer> countList =orderCountVoList.stream().map(OrderCountVo::getCount).collect(Collectors.toList());
等效于:用一个for循环,遍历orderCountVoList,并将每个item的getReserveDate()和getCount()的值,添加到各自的list中。