需求
有一個(gè)部門類,如下
public class Department {
/**
* 部門 id
*/
private String id;
/**
* 部門名稱
*/
private String name;
/**
* 上級(jí)部門id已慢, 頂級(jí)為 null
*/
private String parentId;
... 省略 get set
}
有一組如下的數(shù)據(jù)需要進(jìn)行排序曲聂,排序規(guī)則為,1級(jí)部門后面是所有2級(jí)部門蛇受,如果2級(jí)部門存在下屬3級(jí)部門句葵,則該2級(jí)部門后緊跟著所有3級(jí)部門,以此類推兢仰。
List<Department> originalList = new ArrayList<>();
originalList.add(new Department("7", "國內(nèi)采購部", "5"));
originalList.add(new Department("8", "北上廣采購部", "7"));
originalList.add(new Department("9", "沿海采購部", "7"));
originalList.add(new Department("2", "國外采購部", "5"));
originalList.add(new Department("10", "歐美采購部", "2"));
originalList.add(new Department("11", "澳洲采購部", "2"));
originalList.add(new Department("3", "國內(nèi)銷售部", "6"));
originalList.add(new Department("4", "國外銷售部", "6"));
originalList.add(new Department("1", "運(yùn)營部", null));
originalList.add(new Department("5", "采購部", "1"));
originalList.add(new Department("6", "銷售部", "1"));
排序結(jié)果如下
排序結(jié)果.png
簡(jiǎn)單版
實(shí)現(xiàn)分析
- 找到所有一級(jí)部門
- 遍歷所有一級(jí)部門乍丈,找到是否存在二級(jí)部門刊懈,不存在返回
- 遍歷所有二級(jí)部門庶橱,查找是否存在三級(jí)部門,不存在返回
- 遍歷所有三級(jí)部門卵迂,查找是否存在四級(jí)部門察蹲,不存在返回
- ...
能看出來這是一個(gè)遞歸算法请垛,嘗試用偽代碼實(shí)現(xiàn)下邏輯
List<Department> 排序結(jié)果;
void 查找子部門(上級(jí)部門) {
所有下級(jí)部門 = 查找所有下級(jí)部門()
if (所有下級(jí)部門不存在) {
return;
}
for (下級(jí)部門: 所有下級(jí)部門) {
排序結(jié)果.添加(下級(jí)部門);
查找子部門(下級(jí)部門);
}
}
思路清楚了催训,就是找下級(jí),遍歷下級(jí)宗收,添加下級(jí)到結(jié)果集中漫拭,同時(shí)找下級(jí)的所有下級(jí)
代碼實(shí)現(xiàn)
public class DepartmentSort {
private List<Department> deptList;
private List<Department> resultList = new ArrayList<>();
public DepartmentSort(List<Department> deptList) {
this.deptList = deptList;
}
public static List<Department> sort(List<Department> originalList) {
return new DepartmentSort(originalList).sort();
}
private List<Department> sort() {
findChildren(new Department());
return resultList;
}
/**
* 查詢下級(jí)部門
*
* @param parentDept
*/
private void findChildren(Department parentDept) {
// 查找所有下級(jí)部門
List<Department> childrenList = deptList.stream()
.filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
.collect(Collectors.toList());
if (childrenList.isEmpty())
/* 不存在下級(jí)部門,跳出遞歸 */
return;
else
// 遍歷所有下級(jí)部門混稽,塞入下級(jí)部門 resultList 的同時(shí)采驻,查找所有該下級(jí)部門對(duì)應(yīng)的下級(jí)部門
childrenList.forEach(d -> {
resultList.add(d);
findChildren(d);
});
}
}
編碼測(cè)試
List<Department> resultList = DepartmentSort.sort(originalList);
測(cè)試結(jié)果,達(dá)到了預(yù)期結(jié)果
測(cè)試結(jié)果1.png
優(yōu)化版
接下來匈勋,分析上個(gè)版本代碼的不足
- 上個(gè)版本的實(shí)現(xiàn)思路是用 一個(gè) resultList 集合去存儲(chǔ)所有結(jié)果礼旅, findChildren 方法是返回 void 的
- 但其實(shí)對(duì)于上級(jí)部門,調(diào)用 findChildren 方法洽洁,希望得到的是所有下級(jí)部門以及下級(jí)部門可能存在的所有更下一級(jí)部門的集合
帶著這幾條思路痘系,改造代碼如下
private List<Department> findChildren2(Department parentDept) {
// 查找所有下級(jí)部門
List<Department> childrenList = deptList.stream()
.filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
.collect(Collectors.toList());
List<Department> tempList = new ArrayList<>();
// 遍歷所有下級(jí)
childrenList.forEach(d -> {
tempList.add(d);
tempList.addAll(findChildren2(d));
});
return tempList;
}
經(jīng)過測(cè)試,結(jié)果正確饿自,不貼圖了...
stream 優(yōu)化版
繼續(xù)分析上個(gè)版本代碼汰翠,依然存在不優(yōu)雅的地方
- 需要弄一個(gè) tempList 存儲(chǔ)所有下級(jí)部門以及可能存在的所有更下一級(jí)部門
- 雖然使用了 stream,但是只是用來過濾下級(jí)璃俗,大材小用了
利用 stream 流的思想去考慮問題奴璃,其實(shí)遍歷出來的下級(jí)和可能存在的所有下級(jí)部門的所有下級(jí)部門,是綁定在一起的城豁,也就是 tempList。根據(jù)這樣的關(guān)系抄课,很容易想到 map 操作符唱星,得到 下級(jí)部門.map(下級(jí)部門和所有下級(jí)部門的下級(jí)部門的集合)
帶著這個(gè)思路,實(shí)現(xiàn)如下代碼
public class DepartmentSort2 {
private List<Department> deptList;
public DepartmentSort2(List<Department> deptList) {
this.deptList = deptList;
}
public static List<Department> sort(List<Department> originalList) {
return new DepartmentSort2(originalList).sort();
}
private List<Department> sort() {
return findChildren(new Department())
.collect(Collectors.toList());
}
/**
* 查詢下級(jí)部門
*
* @param parentDept
*/
private Stream<Department> findChildren(Department parentDept) {
return deptList.stream()
.filter(d -> Objects.equals(parentDept.getId(), d.getParentId()))
.flatMap(d -> Stream.concat(Stream.of(d), findChildren(d)));
}
}
編寫測(cè)試代碼
List<Department> resultList2 = DepartmentSort2.sort(originalList);
測(cè)試結(jié)果
stream 版測(cè)試結(jié)果.png