Java 菜单列表转成树形结构

  在工作中菜单多的情况,会遇到list列表转为树形结构的菜单,特此记录下利用反射实现

一、实体类中格式

/**
 * 系统菜单表
 */
@ApiModel(value="系统菜单表")
@Data
public class Menu implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
  
    @TreeField(TreeField.Field.ID)
    @ApiModelProperty(value = "ID")
    private String id;

    @ApiModelProperty(value = "父ID")
    @TreeField(TreeField.Field.PARENDID)
    private String parentId;

   
    @ApiModelProperty(value = "菜单名称")
    private String menuName;

   
    @ApiModelProperty(value = "菜单路径")
    private String menuUrl;

   
    @ApiModelProperty(value = "图标")
    private String menuIcon;

  
    @ApiModelProperty(value = "子菜单")
    private List<Menu> children;

  
}

二、自定义注解(通过反射)

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TreeField {
    TreeField.Field value();

    public static enum Field {
        ID,
        PARENTID,
        CHILDREN;

        private Field() {
        }
    }
}

三、工具类


import java.lang.reflect.Field;
import java.util.*;

public class TreeUtil {
   

    /**
     * list 转 tree
     *
     * @param list 节点集合
     */
    public static <T> List<T> listToTree(List<T> list) {
        List<T> roots = new ArrayList<>();
        if (list == null || list.size() == 0) {
            return roots;
        }
        Class<?> clazz = list.get(0).getClass();
        Field idField = getField(clazz, TreeField.Field.ID);
        Field pIdField = getField(clazz, TreeField.Field.PARENTID);
        Field childrenField = getField(clazz, TreeField.Field.CHILDREN);

        try {
            T parent;
            Map<Object, T> map = new HashMap<>(list.size());
            for (T node : list) {
                map.put(idField.get(node), node);
            }
            for (T node : list) {
                if ((parent = map.get(pIdField.get(node))) != null && !parent.equals(node)) {
                    if (childrenField.get(parent) == null) {
                        childrenField.set(parent, new ArrayList<>());
                    }
                    ((List<T>) childrenField.get(parent)).add(node);
                } else {
                    roots.add(node);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return roots;
    }

    /**
     * tree 转 list
     *
     * @param tree 结构化集合
     */
    public static <T> List<T> treeToList(List<T> tree) {
        List<T> list = new ArrayList<>();
        if (tree == null || tree.size() == 0) {
            return list;
        }
        Class<?> clazz = tree.get(0).getClass();
        Field childrenField = getField(clazz, TreeField.Field.CHILDREN);
        // 使用Stack进行深度遍历(先进后出)
        try {
            Stack<T> stack = new Stack<>();
            for (T root : tree) {
                stack.push(root);
                while (!stack.isEmpty()) {
                    T node = stack.pop();
                    if (childrenField.get(node) != null) {
                        List<T> children = (List<T>) childrenField.get(node);
                        // 子节点倒序入栈
                        Collections.reverse(children);
                        children.forEach(stack::push);
                        childrenField.set(node, null);
                    }
                    list.add(node);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    private static Field getField(Class<?> clazz, TreeField.Field value) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //打开私有访问
            field.setAccessible(true);
            if (field.isAnnotationPresent(TreeField.class)) {
                TreeField anno = field.getAnnotation(TreeField.class);
                if (anno.value() == value) {
                    return field;
                }
            }
        }
        for (Field field : fields) {
            if (field.getName().equalsIgnoreCase(value.toString())) {
                return field;
            }
        }
        return null;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值