在工作中菜单多的情况,会遇到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;
}
}