JAVA构建树形结构通用工具类
场景说明
案例中的方法说明
构建思路实现
第一步:构建接口TreeFeature
第二步:让需要构建树形结构的类实现该接口并实现方法
第三部创建通用的TreeUtil工具类
到这位置,TreeUtil已经完成来测试,先在实体类中添加点测试数据
编写测试类
输出结果
当有一个新的需要构建树形结构的实体时,只需要实现TreeFeature接口并重写好其中的方法,调用TreeUtil .build即可
场景说明
在日常java开发过程中,经常遇到要把一个扁平结构的数据,处理成为树形结构返给前台,因此可以写一个公共的util来实现。特此写一篇博客来记录解决问题的思路以及过程
案例中的方法说明
用了java 8的最新特性lambda表达式,可以了解下其实很简单。
===== list.stream
吧list转化为一个流进行操作
===== list.stream.filter(c -> c.getParentId() != null)
filter 选择出满足条件的元素
构建思路实现
要处理树形结构,对应的表记录和实体类中至少满足以下条件才可以处理
:实体类或表中要包含类似如下的字段
表中结构:节点id:node 、父节点id: parentId 、节点名称:nodeName
实体类中还要多一个childrenList的字段,可以加在Vo里面
第一步:构建接口TreeFeature
若要实现通用构建,则肯定需要获得实体类的parentId,nodeId,childrenList,但是每个实体类或Vo的名字不一样。怎么办呢。因此我想到的是定义一个接口,让需要构建树形结构的实体来实现。当中就有获得nodeId,设置子集的方法。接口定义如下
/**
-
定义一个接口,如果需要构建一颗树需要实现该接口
-
实现接口的需要传入一个泛型参数T ,表示该类实现了该接口
*/
public interface TreeFeature {/**
- 获取子节点
*/
List getChildrenList();
/**
- 获取当前节点
*/
String findNodeId();
/**
- 获取父节点
*/
String findParentId();
/**
- 设置子节点
*/
void putChildrenList(List children);
/**
- 树形结构名称
*/
String getTreeLable();
- 获取子节点
第二步:让需要构建树形结构的类实现该接口并实现方法
public class AreaDemo implements TreeFeature {
//区域编码
private String baCode;
//区域名称
private String baName;
//父节点
private String baParentId;
//子节点
private List<Area> childrenList;
@Override
public List getChildrenList() {
return this.childrenList;
}
@Override
public String findNodeId() {
return this.baCode;
}
@Override
public String findParentId() {
return this.baParentId;
}
@Override
public void putChildrenList(List children) {
this.childrenList = children;
}
@Override
public String getTreeLable() {
return this.baName;
}
第三部创建通用的TreeUtil工具类
1.首先创建一个build方法供外部调用,接受参数为实现了TreeFeature接口的List,和一个根节点参数,返回值为构建好的list,方法先初步筛选出根节点和所有非根节点
if(!CollectionUtils.isEmpty(allList)){
//1.获取所有根节点
List<V> roots = allList.stream().filter(c -> c.getParentId().equals(rootNodeId)).filter(c -> c.getParentId() == null).collect(Collectors.toList());
//2.获取所有非根节点
List<V> others = allList.stream().filter(c -> c.getParentId() != null).collect(Collectors.toList());
return buildTree(roots,others);
}
return new ArrayList<>();
}
2.创建buildTree方法,参数为根节点集合以及非根节点的集合,思路为遍历根节点,吧根节点下的子集加入其中
if(!CollectionUtils.isEmpty(roots)){
Map<String,String> map = new ConcurrentHashMap<>();
//从根节点向下遍历
roots.forEach(beanTree -> addChildren(others,beanTree,map));
return roots;
}
return new ArrayList<>();
}
3.核心关键点来了addChildren为核心方法,顾名思义就是吧子节点加入到根节点当中,因此初步确定该方法接受参数为两个,一个集合,一个根节点。另外为了减少遍历次数,传入一个记录了操作过节点的map
* 把根节点所属的子节点加入其中
* @param others
* @param beanTree
* @param map
* @param <V>
*/
public static <V extends TreeFeature2<V>> void addChildren(List<V> others,V beanTree,Map map){
//定义一个子集集合,最后会调用实体的putChildrenList,给实体的childrenList赋值
List<V> childrenList = new ArrayList<>();
others.stream()
//过滤集合中已经有的节点
.filter(c->!map.containsKey(c.getNode()))
//筛选属于传入的beanTree下的子节点,元素的parentId = beanTree.nodeID
.filter(c->c.getNode().equals(beanTree.getParentId()))
//至此已经从others中过滤出了beanTree下的子节点集合,开始遍历并加入childreList
.forEach(item -> {
//吧该节点放入map
map.put(item.getNode(),item.getParentId());
//递归调用addChildren,继续把子节点的子集加入
addChildren(others,beanTree,map);
childrenList.add(item);
});
//调用beanTree的putChildrenList 给该节点赋值
beanTree.putChildrenList(childrenList);
}
到这位置,TreeUtil已经完成来测试,先在实体类中添加点测试数据
* 初始化
*/
private static List areaList = new ArrayList<>();
static {
areaList.add( new Area("530000","云南省","0"));
areaList.add( new Area("530100","昆明市","530000"));
areaList.add( new Area("530109","曲靖市","530000"));
areaList.add( new Area("530211","麒麟区","530109"));
areaList.add( new Area("530212","麒麟区2","530109"));
areaList.add( new Area("530101","昆明市市辖区","530100"));
areaList.add( new Area("530102","五华区","530100"));
areaList.add(new Area("530102","盘龙区","530100"));
areaList.add( new Area("110000","北京市","0"));
areaList.add( new Area("110100","北京市辖区","110000"));
areaList.add( new Area("110100","北京市辖区","110000"));
areaList.add( new Area("110101","东城区","110100"));
areaList.add( new Area("110102","西城区","110100"));
}
public Area() {
}
public Area(String baCode, String baName, String baParentId) {
this.baCode = baCode;
this.baName = baName;
this.baParentId = baParentId;
}
编写测试类
public static void main(String[] args) {
List<Area> areaList = Area.getAreaList();
List<Area> build = TreeUtilDemo.build(areaList, "0");
build.stream().forEach(item->{
System.out.println(item);
});
}
}
输出结果
Area{baCode=‘110000’, baName=‘北京市’, baParentId=‘0’, childrenList=[Area{baCode=‘110100’, baName=‘北京市辖区’, baParentId=‘110000’, childrenList=[Area{baCode=‘110101’, baName=‘东城区’, baParentId=‘110100’, childrenList=[]}, Area{baCode=‘110102’, baName=‘西城区’, baParentId=‘110100’, childrenList=[]}]}]}
当有一个新的需要构建树形结构的实体时,只需要实现TreeFeature接口并重写好其中的方法,调用TreeUtil .build即可
————————————————
版权声明:本文为CSDN博主「鱼塘霸主」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/postivedragon/article/details/117600590