背景
实际开发中经常会遇到需要由List转换成带父子级结构的树状数据,甚至于需要带父子级结构的搜索结果。本文针对这一场景提供了一种通用的list转换树结构的思路,希望可以对大家有所帮助。如有更好的方案或者可优化点欢迎在下方评论区留言。
实现
废话不多说,直接上代码。
TreeUtils.java - 实现该功能的主类,提供了转换的具体实现和一个用于扩展的接口。
package com.zjtx.tech.utils;
import com.sun.istack.internal.NotNull;
import java.util.*;
/**
* @author 泽济天下
* @date 2020年08月24日16:55
* @description:
*/
public class TreeUtils<V> {
private List<V> selectList;
private List<V> allDataList;
private DataHelper<V> dataHelper;
/**
* 提供多参构造方法
* @param selectList 选中的或者搜索出来的
* @param allDataList
* @param dataHelper
*/
public TreeUtils(List<V> selectList, @NotNull List<V> allDataList, DataHelper<V> dataHelper){
this.selectList = selectList;
this.allDataList = allDataList;
this.dataHelper = dataHelper;
}
public List<V> convertTree(){
//如果选中或者查询结果是空 直接返回
if(selectList == null || selectList.size() == 0){
return selectList;
}
Map<String, V> allDataMap = new LinkedHashMap<>();
//list转存map 这里的list是所有数据的list
for(V v: allDataList){
String key = dataHelper.getKey(v);
if(null == key){
System.out.println("检测到存在key为空的数据,程序即将退出....");
break;
}
allDataMap.put(dataHelper.getKey(v), v);
}
//转换前执行的操作 如果有需要的话
if(dataHelper != null){
dataHelper.before(allDataList, allDataMap);
}
Map<String, V> retMap = new LinkedHashMap<>();
for (V item : selectList) {
updateItemInfo(allDataMap, item, retMap);
}
List<V> result = new ArrayList<>(retMap.values());
if(dataHelper != null){
dataHelper.after(result);
}
return result;
}
/**
* 更新节点信息
* @param allDataMap
* @param item
* @param retMap
*/
private void updateItemInfo(Map<String,V> allDataMap, V item, Map<String,V> retMap) {
V v = allDataMap.get(dataHelper.getKey(item));
if(v == null){
System.out.println("allDataMap中获取指定key的数据返回空: " + dataHelper.getKey(item));
return;
}
//判断是不是顶级节点
String pid = dataHelper.getParentKey(item);
if (pid == null || "0".equals(pid)) {
//是顶级节点直接放到结果map里
retMap.put(dataHelper.getKey(item), v);
return;
}
//不是顶级节点-找到它的父
V parent = allDataMap.get(pid);
if(parent == null){
System.out.println("allDataMap中获取指定key-pid的数据返回空: " + pid);
System.out.println("异常数据信息:" + v);
return;
}
//找到它的所有子
List<V> children = dataHelper.getChildren(parent);
if (children == null) {
children = new LinkedList<>();
}
//替换指定元素
if(children.size() > 0){
int index = -1;
for (int i = 0; i < children.size(); i++) {
if(dataHelper.getKey(children.get(i)).equals(dataHelper.getKey(item))){
index = i;
break;
}
}
if(index != -1){
children.remove(index);
children.add(index, v);
}else {
children.add(v);
}
} else {
children.add(v);
}
dataHelper.setChildren(children, parent);
allDataMap.put(pid, parent);
updateItemInfo(allDataMap, parent, retMap);
}
public interface DataHelper<V> {
/**
* 转换之前的数据处理
* @param srcData
* @param srcMap
*/
void before(List<V> srcData, Map<String, V> srcMap);
/**
* 转换之后的数据处理
* @param resultList
*/
void after(Collection<V> resultList);
/**
* 获取主键
* @param item
* @return
*/
String getKey(V item);
/**
* 获取父键
* @param item
* @return
*/
String getParentKey(V item);
/**
* 获取所有的子
* @param item
* @return
*/
List<V> getChildren(V item);
/**
* 重新设置子
* @param children
* @param item
*/
void setChildren(List<V> children, V item);
}
}
测试
下面我们写个测试数据测试下,以菜单为例。
测试的main方法类
package com.zjtx.tech.utils;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.List;
/**
* @author 泽济天下
* @title TreeUtilsTest
* @description list转换树工具测试类
* @date 2020/8/24 0024 下午 21:24
*/
public class TreeUtilsTest {
public static void main(String[] args) {
//正常场景中 list都是查询出来的
//这里的参数有两个list 一个是所有的数据allDataList 还有个选中的selectList
//如果两个list一样就是返回完整的树结构 如果两个不一样会返回selectList对应的所有父子级结构
//这个测试类只演示了返回完整树结构的操作 其他情况大家可以自己尝试下
List<Menu> list = initMenuList();
TreeUtils<Menu> treeUtils = new TreeUtils<>(list, list, new MenuDataHelper());
List<Menu> menus = treeUtils.convertTree();
String o = JSON.toJSON(menus).toString();
System.out.println(o);
}
private static List<Menu> initMenuList() {
List<Menu> list = new ArrayList<>();
Menu menu1 = new Menu();
menu1.setId("1");
menu1.setParentId("0");
menu1.setName("菜单1");
list.add(menu1);
Menu menu2 = new Menu();
menu2.setId("2");
menu2.setParentId("0");
menu2.setName("菜单2");
list.add(menu2);
Menu menu3 = new Menu();
menu3.setId("3");
menu3.setParentId("1");
menu3.setName("菜单11");
list.add(menu3);
Menu menu4 = new Menu();
menu4.setId("4");
menu4.setParentId("3");
menu4.setName("菜单111");
list.add(menu4);
return list;
}
}
涉及到的实体类
package com.zjtx.tech.utils;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author 泽济天下
* @title Menus
* @description TODO
* @date 2020/8/24 0024 下午 21:31
*/
public class Menu {
private String id;
private String name;
private String parentId;
private List<Menu> subMenus;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public List<Menu> getSubMenus() {
return subMenus;
}
public void setSubMenus(List<Menu> subMenus) {
this.subMenus = subMenus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
还有个DataHelper的实现类,也比较简单,主要做了属性的转换。
package com.zjtx.tech.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author 泽济天下
* @title MenuDataHelper
* @description TODO
* @date 2020/8/24 0024 下午 21:37
*/
public class MenuDataHelper implements TreeUtils.DataHelper<Menu> {
@Override
public void before(List<Menu> srcData, Map<String, Menu> srcMap) {
}
@Override
public void after(Collection<Menu> resultList) {
}
@Override
public String getKey(Menu item) {
return item.getId();
}
@Override
public String getParentKey(Menu item) {
return item.getParentId();
}
@Override
public List<Menu> getChildren(Menu item) {
return item.getSubMenus();
}
@Override
public void setChildren(List<Menu> children, Menu item) {
item.setSubMenus(children);
}
}
运行结果:
[{"subMenus":[{"subMenus":[{"name":"菜单111","id":"4","parentId":"3"}],"name":"菜单11","id":"3","parentId":"1"}],"name":"菜单1","id":"1","parentId":"0"},{"name":"菜单2","id":"2","parentId":"0"}]
以上就是所有内容,如果有更好的建议的话欢迎评论区留言。
如果有其他需求也可以留言反馈,共同讨论实现方案。