開發記錄:關於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中。
