背景
最近遇到了一個分層級展示指標的需求,前端使用el-tree樹形組件,要求按官方文檔的格式提供數據。
數據格式:
id: 1,
label: '一級 1',
children:
id: 4,
label: '二級 1-1',
children:
id: 9,
label: '三級 1-1-1',
children: ...
封裝思路
1、首先我們需要獲取到所有的節點,新建一個集合result來保存所有頂級節點,也就是parentId為空的或指定值。
2、然后我們需要找出二級節點存入到頂級節點的childList中,找到三級節點存入到二級節點的childList中,依次類推,最后將result返回。
代碼實現:
1、設計實體類
@Data
public class Evaluation {
private String id;
private String label;
private String parentId;
private List<Evaluation> childList;
}
2、業務代碼
public class EvaluationService {
private EvaluationDao evaluationDao;
public List<Evaluation> getEvaluations() {
// 查出所有指標
List<Evaluation> all = evaluationDao.findAll();
// 把所有的數據都放到map中
Map<String, Evaluation> treeMap = new HashMap<>();
for (int i = 0; i < all.size() && !all.isEmpty(); i++) {
// 元素的id為鍵,元素本身為值
treeMap.put(all.get(i).getId(), all.get(i));
}
// 將所有頂層節點存入result中
List<Evaluation> result = new ArrayList<>();
// 遍歷map得到頂層節點或者游離節點
for (int i = 0; i < all.size(); i++) {
if (!treeMap.containsKey(all.get(i).getParentId())) {
result.add(all.get(i));
}
}
// 遍歷數據,將對象放入父級節點的childList屬性中
for (int i = 0; i < all.size() && !all.isEmpty(); i++) {
Evaluation Evaluation = treeMap.get(all.get(i).getParentId());
if (Evaluation != null) {
// 有父節點,校驗父節點下childList是否存在,然后將子節點放入
if (Evaluation.getChildList() == null) {
Evaluation.setChildList(new ArrayList<>());
}
// 添加到父節點的ChildList集合下
Evaluation.getChildList().add(all.get(i));
}
}
return result;
}
}
如果項目中使用的不多的話,這樣就可以了。實現方式可以有很多種,用上邊的方式的好處在於免去了多次到數據庫查詢的操作,而且可以靈活封裝多層級數據,二層、三層、五層等等都可以。
封裝工具類
為了實現代碼的復用,我們可以將上方代碼封裝成工具類。
DataTree接口:
寫這個接口類主要是為了下面的工具類,定義泛型T的類型
public interface DataTree<T> {
public String getId();
public String getParentId();
public void setChildList(List<T> childList);
public List<T> getChildList();
}
工具類實現:
以下的泛型T就是接收數據的實體類,要繼承上面數據接口類
public class TreeUtils {
//獲取頂層節點
public static <T extends DataTree<T>> List<T> getTreeList(String topId, List<T> entityList) {
List<T> resultList = new ArrayList<>();
Map<Object, T> treeMap = new HashMap<>();
T itemTree;
for (int i = 0; i < entityList.size() && !entityList.isEmpty(); i++) {
itemTree = entityList.get(i);
//把所有的數據放到treeMap中,id為key
treeMap.put(itemTree.getId(), itemTree);
//把頂層節點放到集合resultList中
if (topId.equals(itemTree.getParentId()) || itemTree.getParentId() == null) {
resultList.add(itemTree);
}
}
//循環數據,把數據放到上一級的childen屬性中
for (int i = 0; i < entityList.size() && !entityList.isEmpty(); i++) {
itemTree = entityList.get(i);
T data = treeMap.get(itemTree.getParentId());
// 不等於null,也就意味着有父節點
if (data != null) {
if (data.getChildList() == null) {
data.setChildList(new ArrayList<>());
}
//把子節點 放到父節點childList當中
data.getChildList().add(itemTree);
//把放好的數據放回map當中
treeMap.put(itemTree.getParentId(), data);
}
}
return resultList;
}
}
使用方式:
1、實體類:
設計實體類,實現之前定義好的DataTree接口
@Data
public class Evaluation implements DataTree<Evaluation> {
private String id;
private String label;
private String parentId;
private List<Evaluation> childList;
}
2、調用TreeUtils,傳入數據和頂層節點id,即可獲取到所需要的數據結構。
public class Test {
private EvaluationDao evaluationDao;
public List<Evaluation> getEvaluations() {
// 接收在數據庫中查詢到的數據
List<Evaluation> data = evaluationDao.findAll();
// 調用工具類,第一個參數是默認傳入的頂級id,和查詢出來的數據
return TreeUtils.getTreeList("0", data);
}
}