文章介绍
本文将数据list转成树形结构数据。 必要条件:实体类结构必须有基本树形数据结构必要字段 例如:节点id字段、父节点id字段、子 节点集合字段。
自定义注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 通过注解构建实体类树形数据结构
*
* 只需在实体类中 节点字段、父节点字段、子节点集合字段 加上本注解,并填充 type属性值即可
* 节点字段: 支持类型 String、Long 、int 、Integer
* 父节点字段: 支持类型 String、Long 、int 、Integer
* 子集合字段: 支持类型 List<实体类>、List<Object>
*/
@Target(value= {ElementType.FIELD})
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TreeElement {
/**
* 字段类型 引用 TreeColumnType 中的常量
* @return
*/
String type() default "";
}
使用到的常量接口
/**
* @TreeElement注解type属性 标注值
*/
public interface TreeColumnType {
/**
* 节点id
*/
String NODE_ID = "NODE_ID";
/**
* 父节点id
*/
String PARENT_ID = "PARENT_ID";
/**
* 子节点集合
*/
String CHILDREN_ARRAY = "CHILDREN_ARRAY";
}
树形数据构建工具类
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class TreeUtils {
/**
* 将列表转换为列表树/Convert List to List Tree
* @param trees 生成树形结构数据源
* @param boot 子集合无数据时 boot等于true返回null,boot等于false返回空数组
* @param clas 树形数据结构对象 即 @TreeElement 注解的使用类
* @param maxDeep 最大递归深度 为null则不限制
* @return result
* @throws IllegalAccessException result
*/
public static <T> List<T> ListToTree(List<T> trees,boolean boot,Integer maxDeep,Class<T> clas) throws IllegalAccessException, NoSuchFieldException {
HashMap<String,String> keys = findTreeElement(clas);
List<Object> ids = findColumnValues(trees,keys.get(TreeColumnType.NODE_ID));
List<T> rootList = new ArrayList<T>();
HashMap<Object, List<T>> pidAndTrees= new HashMap<>();
Object value = null;
for (final T t : trees) {
value = getValue(t, keys.get(TreeColumnType.PARENT_ID));
if(value!=null){
if(pidAndTrees.get(value)!=null) {
pidAndTrees.get(value).add(t);
}else{
pidAndTrees.put(value,new ArrayList<T>(){{this.add(t);}});
}
if (!ids.contains(value)) {
rootList.add(t);
}
}
}
buildChilTree(rootList,pidAndTrees,keys.get(TreeColumnType.NODE_ID),keys.get(TreeColumnType.CHILDREN_ARRAY), boot,0,maxDeep);
return rootList;
}
/**
* 获取指定实体类 @TreeElement注解标注的字段名称
* @param clas
* @param <T>
* @return
*/
private static <T> HashMap<String,String> findTreeElement(Class<T> clas){
Field[] fields = clas.getDeclaredFields();
HashMap<String,String> keys = new HashMap<>(3);
TreeElement treeElement = null;
for (Field field : fields) {
treeElement = field.getAnnotation(TreeElement.class);
if (null != treeElement){
switch (treeElement.type()){
case TreeColumnType.NODE_ID:
keys.put(TreeColumnType.NODE_ID,field.getName());
break;
case TreeColumnType.PARENT_ID:
keys.put(TreeColumnType.PARENT_ID,field.getName());
break;
case TreeColumnType.CHILDREN_ARRAY:
keys.put(TreeColumnType.CHILDREN_ARRAY,field.getName());
break;
default:
break;
}
}
if (keys.size()==3){
break;
}
}
return keys;
}
/**
* 提取数据源中指定字段值
* @param ts 源数据集合
* @param key 需提取值的字段名称
* @param <T>
* @return
* @throws IllegalAccessException
* @throws NoSuchFieldException
*/
private static <T> List<Object> findColumnValues(List<T> ts,String key) throws IllegalAccessException, NoSuchFieldException {
List<Object> ids = new ArrayList<>();
Field field = null;
for (T t : ts) {
field = t.getClass().getDeclaredField(key);
field.setAccessible(true);
ids.add(field.get(t));
}
return ids;
}
/**
* 获取与对象的指定注释值对应的字段值
* Get the field value corresponding to the specified annotation value of the object
* @param t
* @param key
* @param <T>
* @return
* @throws IllegalAccessException
*/
private static <T> Object getValue(T t,String key) throws IllegalAccessException, NoSuchFieldException {
Field field = t.getClass().getDeclaredField(key);
field.setAccessible(true);
Object o = field.get(t);
return o;
}
/**
* 设置与对象的指定注释值对应的字段值
* Get the field value corresponding to the specified annotation value of the object
* @param t
* @param key
* @param <T>
* @return
* @throws IllegalAccessException
*/
private static <T> Object setValue(T t,String key,boolean boot) throws IllegalAccessException, NoSuchFieldException {
Field field = t.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(t, boot ? null:new ArrayList<>());
return null;
}
/**
* 获取对象的指定字段对应的字段类型
* Get the field type corresponding to the specified field of the object
* @param t
* @param key
* @param <T>
* @return
* @throws IllegalAccessException
*/
public static <T> Object getType(T t,String key) throws IllegalAccessException, NoSuchFieldException {
Field field = t.getClass().getDeclaredField(key);
field.setAccessible(true);
return field.getType().toString();
}
/**
* 递归构造/Recursive construction
* @param currentTrees
* @param trees
* @param <T>
* @param deep – 已递归深度
* @param maxDeep – 最大递归深度 可能为null即不限制
* @throws IllegalAccessException
*/
private static <T> void buildChilTree(List<T> currentTrees, HashMap<Object, List<T>> trees,String id,String children,boolean boot,int deep,Integer maxDeep) throws IllegalAccessException, NoSuchFieldException {
if (maxDeep != null && deep >= maxDeep) {
return;
}
Object currentId = null;
for (T t : currentTrees) {
currentId = getValue(t, id);
if(trees.get(currentId)!=null){
List list = (List) getValue(t, children);
list.addAll(trees.get(currentId));
buildChilTree(trees.get(currentId),trees,id,children,boot,deep+1,maxDeep);
}else {
setValue(t, children,boot);
}
}
}
}
实体类样式
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class TestVo
{
/** 节点名称 */
private String name;
/** 节点id 等于0时代表一级根节点*/
@TreeElement(type = TreeColumnType.NODE_ID)
private Long id;
/** 父节点id */
@TreeElement(type = TreeColumnType.PARENT_ID)
private Long parentId;
/** 子节点集合 */
@TreeElement(type = TreeColumnType.CHILDREN_ARRAY)
private List<TbTownshipVillage> children = new ArrayList<>();
public List<TbTownshipVillage> getChildren() {
return children;
}
public void setChildren(List<TbTownshipVillage> children) {
this.children = children;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setParentId(Long parentId)
{
this.parentId = parentId;
}
public Long getParentId()
{
return parentId;
}
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
}
调用代码
List<TestVo> listVo = new ArrayList<>(); //listVo 中数据自行填充或从数据库查询
List<TestVo> list = TreeUtils.ListToTree(listVo, false,null,TestVo.class);