树形结构就是有层级关系的数据结构。
可以解释为:
所有数据构成一颗大树,这颗大树又是由十颗小树组成的;
这十颗小树的层级为1,并且是排序的,其pId
是0,pId
是自身与上一颗树的关联,0表示顶层,其值必须为唯一字段;
其中每一颗小树又是由另外若干小小树组成,小小树层级为2,并且是排序的,其pId
是它上一级(小树)的id
;
依次类推,就形成了一整颗数。
要想构建一棵树,必须有以下字段:
- level :层级
- sort :排序
- pId :上一级的id
- path :path表示为该树在一整颗数上的路径。
- eg:A下面有B,B下面有C和D,假如
path
构成规则为:以id
构成path
,并且以/
隔开,那么D的path
为:/id-a/id-b/id-d/
- path的构成规则及其重要,实际项目中需根据需求灵活使用,可以为
id
或者其他字段,方便查询。最后构成的path必须唯一
- eg:A下面有B,B下面有C和D,假如
- rootId :其实
rootId
并不是必须的,但是在实际使用中发现,加入这个字段以后,可以简化很多代码!这个字段记录的是root树
的唯一标识id
,在分页和查询中十分好用。
展示类似于下图
树结构.png其中有笑gfff笑
,哈哈
,呵呵
三棵树,展开哈哈
,发现有三层。
在Java中,构建一个实体类A的DTO,继承A实现轮子中的方法,增加children
属性用来存放子级。
构建树的轮子:
public class TreeUtils<T extends TreeUtils.Tree> {
/**
* 只是适用于按顺序目录结构排好序的查询方式
*
* @param objects 查询的全部目录(平行结构),按path降序排序
* @param maxLevel 最大层级
* @return
*/
public List<T> createTree(List<T> objects, Integer maxLevel) {
//List必须是根据codePath倒序查询得出;
Map<Integer, List<T>> map = new HashMap<>();
for (int i = 0; i < maxLevel; i++) {//构造层级容器
map.put(i + 1, new ArrayList<T>());
}
Integer tempLevel = maxLevel + 1;//构造临时层级,为了开个头
for (T current : objects) {
Integer currentLv = current.getLevel();
if (currentLv < tempLevel) {//当前层级小于临时层级
List<T> children = map.get(currentLv + 1);//先拿子节点
current.setChildren(children);//将子节点并入所在层级集合
map.put(currentLv + 1, new ArrayList<T>());//然后清空子节点
}
map.get(currentLv).add(current);//将自己并入所在层级集合
map.get(currentLv).sort((a, b) -> a.getSort() - b.getSort());//排序
tempLevel = currentLv;//重新构造层级
}
List<T> list = map.get(1);
return list;
}
/**
* 只适用于从根节点一次清查询出来的list,不需要排好序
*
* @param objects
* @return
*/
public List<T> createTree(List<T> objects) {
return createTree(objects, 1, "0");
}
/**
* 适用于从某个节点一次清查询出来的list,不需要排好序
*
* @param objects
* @return
*/
public List<T> createTree(List<T> objects, Integer startLevel, String pid) {
Map<Integer, Map<String, List<T>>> levelMap = new HashedMap();//保存某一级的map结构 map<三级,map<pid,子目录>>
for (T t : objects) {
Integer currentLevel = t.getLevel();//当前层级
if (levelMap.get(currentLevel) == null) {
levelMap.put(currentLevel, new HashMap<>());
}
if (levelMap.get(currentLevel).get(t.getpId()) == null) {
levelMap.get(currentLevel).put(t.getpId(), new ArrayList<T>());
}
levelMap.get(currentLevel).get(t.getpId()).add(t);
}
List<T> roots = levelMap.get(startLevel).get(pid);
roots.forEach(a -> addChildren(a, levelMap));
roots.sort((a, b) -> a.getSort() - b.getSort());
return roots;
}
void addChildren(T t, Map<Integer, Map<String, List<T>>> levelMap) {
Map<String, List<T>> chidrenMap = levelMap.get(t.getLevel() + 1);
if (chidrenMap != null) {
List<T> children = chidrenMap.get(t.getId());
if (children != null) {
children.forEach((child) -> addChildren(child, levelMap));
children.sort((a, b) -> a.getSort() - b.getSort());
t.setChildren(children);
}
}
return;
}
public interface Tree<T extends TreeUtils.Tree> {
String getId();
Integer getSort();
String getpId();
Integer getLevel();
// String getPath();
List<T> getChildren();
void setChildren(List<T> childrenList);
}
/**
* 只是适用于按顺序目录结构排好序的查询方式
* 如没子集一律返回空集合
*
* @param objects 查询的全部目录(平行结构),按path降序排序
* @param maxLevel 最大层级
* @return
*/
public List<T> createTrees(List<T> objects, Integer maxLevel) {
//List必须是根据codePath倒序查询得出;
Map<Integer, List<T>> map = new HashMap<>();
for (int i = 0; i < maxLevel; i++) {//构造层级容器
map.put(i + 1, new ArrayList<T>());
}
Integer tempLevel = maxLevel + 1;//构造临时层级,为了开个头
for (T current : objects) {
Integer currentLv = current.getLevel();
if (currentLv < tempLevel) {//当前层级小于临时层级
List<T> children = map.get(currentLv + 1);//先拿子节点
if (children == null) {
children = new ArrayList<>();
}
current.setChildren(children);//将子节点并入所在层级集合
map.put(currentLv + 1, new ArrayList<T>());//然后清空子节点
}
map.get(currentLv).add(current);//将自己并入所在层级集合
map.get(currentLv).sort((a, b) -> a.getSort() - b.getSort());//排序
tempLevel = currentLv;//重新构造层级
}
List<T> list = map.get(1);
return list;
}
}
实体类:
public class EpcSystemDictionary extends SuperEntity{
private String id;
private Date createTime;
private Date updateTime;
private String code; //编号
private String name; //名称
private Integer sort;//排序
private String pId;//父id
private String enName;
private Integer isDelete;
private Integer level;//层级
private String path;
public EpcSystemDictionary() {
}
public EpcSystemDictionary(String id, String code, String name,String enName, Integer sort, String pId, Integer level, String path) {
this.id = id;
this.code = code;
this.name = name;
this.enName=enName;
this.sort = sort;
this.pId = pId;
this.level = level;
this.path = path;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSort() {
return sort;
}
public void setSort(Integer sort) {
this.sort = sort;
}
public String getpId() {
return pId;
}
public void setpId(String pId) {
this.pId = pId;
}
public String getEnName() {
return enName;
}
public void setEnName(String enName) {
this.enName = enName;
}
public Integer getIsDelete() {
return isDelete;
}
public void setIsDelete(Integer isDelete) {
this.isDelete = isDelete;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
DTO:
public class EpcSystemDictionaryDto extends EpcSystemDictionary implements TreeUtils.Tree<EpcSystemDictionaryDto> {
List<EpcSystemDictionaryDto> children = new ArrayList<>();
@Override
public List<EpcSystemDictionaryDto> getChildren() {
return children;
}
@Override
public void setChildren(List<EpcSystemDictionaryDto> childrenList) {
this.children=childrenList;
}
}
调用:
/**
* 获取字典某一个根列表
*
* @param
* @return
*/
@Override
public EpcSystemDictionaryDto getOneRoot(String name, Long companyId) {
//避免写接口地址的时候传入的name后面没有写“/”
name = name.length() == (name.lastIndexOf("/") + 1) ? name : name + "/";
List<EpcSystemDictionaryDto> epcSystemDictionaryList = epcSystemDictionaryMapper.selectOneRoot(name, companyId);
for (EpcSystemDictionaryDto e : epcSystemDictionaryList) {
if (e.getPath().equals(name)) {
return new TreeUtils<EpcSystemDictionaryDto>().createTree(epcSystemDictionaryList, e.getLevel(), e.getpId()).get(0);
}
}
return new EpcSystemDictionaryDto();
}
代码其实不难,难的是操作数据的思想,对数据结构变化的把握,通过数据的现时情况得到自己想要的结果。
路漫漫其修远兮,吾将上下而求索。
网友评论