赞
踩
不废话,直接上代码
可以直接使用list2tree()实现列表转树形结构
package com.server.utils.tree; import org.springframework.beans.BeanUtils; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; /** * @author visy.wang * @date 2024/6/27 21:27 */ public class TreeUtil { //通过Map的方式组装树形结构(只需单次遍历即可完成) public static <T,K,R> R list2tree(List<T> list, K rootId, Function<T,K> idGetter, Function<T,K> pidGetter, Function<T,R> converter, Supplier<R> builder, BiConsumer<R,R> childAdder){ Map<K, R> map = new HashMap<>(); for (T t : list) { K id = idGetter.apply(t), pid = pidGetter.apply(t); //查找当前节点 R node = map.get(id); if(node == null){//当前节点不存在则创建 node = converter.apply(t); map.put(id, node); }else{//当前节点已存在(被其他节点以父节点加入),补全剩余字段 R srcNode = converter.apply(t); BeanUtils.copyProperties(srcNode, node, getNullProperties(srcNode)); } //查找父节点 R parent = map.get(pid); if(parent == null){//父节点不存在,则创建父节点,并将自身添加到父节点的子节点集合中 parent = builder.get(); childAdder.accept(parent, node); map.put(pid, parent); }else{//父节点已存在,直接将自身添加到父节点的子节点集合中 childAdder.accept(parent, node); } } return map.get(rootId); } //通过递归的方式组装树形结构(层级过多时占用内存较大,数据不规范时有内存溢出风险) public static <T,K,R> List<R> list2tree(List<T> list, K rootId, Function<T,K> idGetter, Function<T,K> pidGetter, Function<T,R> converter, BiConsumer<R,List<R>> childrenSetter){ return list.stream().filter(t -> { K parentId = pidGetter.apply(t); return Objects.equals(parentId, rootId); }).map(t -> { K id = idGetter.apply(t); R node = converter.apply(t); List<R> children = list2tree(list, id, idGetter, pidGetter, converter, childrenSetter); if(!children.isEmpty()){ childrenSetter.accept(node, children); } return node; }).collect(Collectors.toList()); } //通过Map+实现接口的方式组装树形结构 public static <T,K> List<TreeNode<K>> list2tree(List<T> list, K rootId, Function<T,TreeNode<K>> converter, Supplier<TreeNode<K>> builder){ Map<K, TreeNode<K>> map = new HashMap<>(); for (T t : list) { TreeNode<K> node = converter.apply(t); K id = node.getId(), parentId = node.getParentId(); //查找当前节点 TreeNode<K> currNode = map.get(id); if(currNode != null){//当前节点已存在(被其他节点以父节点加入) //复制子节点集合 node.setChildren(currNode.getChildren()); } map.put(id, node);//更新或添加当前节点 //查找父节点 TreeNode<K> parentNode = map.get(parentId); if(parentNode == null){//父节点不存在,则创建父节点,并将自身添加到父节点的子节点集合中 parentNode = builder.get(); parentNode.addChild(node); map.put(parentId, parentNode); }else{//父节点已存在,直接将自身添加到父节点的子节点集合中 parentNode.addChild(node); } } TreeNode<K> rootNode = map.get(rootId); return rootNode==null ? Collections.emptyList() : rootNode.getChildren(); } //通过递归+实现接口的方式组装树形结构 public static <T,K> List<TreeNode<K>> list2tree(List<T> list, K rootId, Function<T,TreeNode<K>> converter){ return list.stream().map(converter).filter(node -> { K parentId = node.getParentId(); return Objects.equals(parentId, rootId); }).peek(node -> { K id = node.getId(); List<TreeNode<K>> children = list2tree(list, id, converter); if(!children.isEmpty()){ node.setChildren(children); } }).collect(Collectors.toList()); } private static final Map<String,Field[]> fieldsCache = new HashMap<>(); private static String[] getNullProperties(Object obj) { Class<?> clazz = obj.getClass(); String className = clazz.getName(); Field[] fields = fieldsCache.get(className); if(fields == null){ fields = clazz.getDeclaredFields(); Field.setAccessible(fields, true); fieldsCache.put(className, fields); } List<String> nullProperties = new ArrayList<>(); for (Field field : fields) { try { Object value = field.get(obj); if (value == null) { nullProperties.add(field.getName()); } } catch (IllegalAccessException e) { e.printStackTrace(); } } String[] result = new String[nullProperties.size()]; return nullProperties.toArray(result); } }
定义节点规范
package com.server.utils.tree; import java.util.List; /** * @author visy.wang * @date 2024/7/1 10:45 */ public interface TreeNode<K> { K getId(); K getParentId(); void addChild(TreeNode<K> child); List<TreeNode<K>> getChildren(); void setChildren(List<TreeNode<K>> children); }
package com.server.utils.tree; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 菜单 */ @Data @NoArgsConstructor @AllArgsConstructor public class Menu implements Serializable { private static final long serialVersionUID = 1L; /** * 菜单id */ private Long id; /** * 父id */ private Long fid; /** * 机构名称 */ private String name; /** * 模块id */ private Integer level; /** * 状态 1 启用 2 停用 */ private Integer status; /** * 权重 */ private Integer weight; }
package com.server.utils.tree; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.ArrayList; import java.util.List; /** * @author visy.wang * @date 2024/6/27 21:54 */ @Data @EqualsAndHashCode(callSuper = true) public class MenuNode extends Menu { //不一定要继承原始对象(字段都能复用的时候才考虑继承) /** * 是否勾选 */ private Integer isCheck; /** * 子菜单列表 */ private List<MenuNode> children; public void addChild(MenuNode child){ if(children == null){ children = new ArrayList<>(); } children.add(child); } }
package com.server.utils.tree; import com.alibaba.fastjson.JSON; import org.springframework.beans.BeanUtils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author visy.wang * @date 2024/6/27 21:55 */ public class Test { public static void main(String[] args) { List<Menu> menuList = new ArrayList<>(); //顺序可以任意调整,不影响结果 menuList.add(new Menu(1L, null, "菜单A", 1, 1,1)); menuList.add(new Menu(4L, 2L, "菜单BA", 2, 1,4)); menuList.add(new Menu(3L, 1L, "菜单AA", 2, 1,3)); menuList.add(new Menu(5L, 3L, "菜单AAA", 3, 1,5)); menuList.add(new Menu(2L, null, "菜单B", 1, 1,2)); //勾选的菜单ID集合 Set<Long> checkedMenuIds = new HashSet<>(); checkedMenuIds.add(3L); checkedMenuIds.add(5L); //map的方式 MenuNode root = TreeUtil.list2tree( menuList, //原始列表 null, //根节点ID,用于提取顶层节点 Menu::getId, //获取ID的方法,也可以指定别的字段 Menu::getFid, //获取父ID的方法,也可以指定别的字段,但是必须和上面的方法对应 menu -> { //将列表中的原始对象转换成节点对象(一般来说比原始对象多了对子节点集合的持有,除此之外也可以按需要增减字段) MenuNode node = new MenuNode();//创建一个节点 BeanUtils.copyProperties(menu, node);//复制原始对象的字段到节点对象 node.setIsCheck(checkedMenuIds.contains(menu.getId()) ? 1 : 0);//单独设置其他字段 return node;//返回节点对象 }, MenuNode::new, //节点对象的构造方法,用于创建一个新的父节点对象 MenuNode::addChild //添加子节点的方法 ); System.out.println(JSON.toJSONString(root.getChildren())); //递归的方式 List<MenuNode> menuNodeList = TreeUtil.list2tree( menuList, //原始列表 null, //根节点ID Menu::getId, //获取ID的方法,也可以指定别的字段 Menu::getFid, //获取父ID的方法,也可以指定别的字段,但是必须和上面的方法对应 menu -> { //将列表中的原始对象转换成节点对象(一般来说比原始对象多了对子节点集合的持有,除此之外也可以按需要增减字段) MenuNode node = new MenuNode();//创建一个节点 BeanUtils.copyProperties(menu, node);//复制原始对象的字段到节点对象 node.setIsCheck(checkedMenuIds.contains(menu.getId()) ? 1 : 0);//单独设置其他字段 return node;//返回节点对象 }, MenuNode::setChildren //设置子节点集合的方法 ); System.out.println(JSON.toJSONString(menuNodeList)); } }
[ { "children": [ { "children": [ { "fid": 3, "id": 5, "isCheck": 1, "level": 3, "name": "菜单AAA", "status": 1, "weight": 5 } ], "fid": 1, "id": 3, "isCheck": 1, "level": 2, "name": "菜单AA", "status": 1, "weight": 3 } ], "id": 1, "isCheck": 0, "level": 1, "name": "菜单A", "status": 1, "weight": 1 }, { "children": [ { "fid": 2, "id": 4, "isCheck": 0, "level": 2, "name": "菜单BA", "status": 1, "weight": 4 } ], "id": 2, "isCheck": 0, "level": 1, "name": "菜单B", "status": 1, "weight": 2 } ]
[ { "children": [ { "children": [ { "fid": 3, "id": 5, "isCheck": 1, "level": 3, "name": "菜单AAA", "status": 1, "weight": 5 } ], "fid": 1, "id": 3, "isCheck": 1, "level": 2, "name": "菜单AA", "status": 1, "weight": 3 } ], "id": 1, "isCheck": 0, "level": 1, "name": "菜单A", "status": 1, "weight": 1 }, { "children": [ { "fid": 2, "id": 4, "isCheck": 0, "level": 2, "name": "菜单BA", "status": 1, "weight": 4 } ], "id": 2, "isCheck": 0, "level": 1, "name": "菜单B", "status": 1, "weight": 2 } ]
节点对象必须实现TreeNode接口,泛型中指定子父关联字段的类型
package com.server.utils.tree; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.ArrayList; import java.util.List; /** * @author visy.wang * @date 2024/7/1 11:31 */ @Data @EqualsAndHashCode(callSuper = true) public class MenuNodeV2 extends Menu implements TreeNode<Long>{ /** * 是否勾选 */ private Integer isCheck; /** * 子菜单列表 */ private List<TreeNode<Long>> children; @Override public Long getParentId() { return getFid(); } @Override public void addChild(TreeNode<Long> child) { if(this.children == null){ this.children = new ArrayList<>(); } this.children.add(child); } }
package com.server.utils.tree; import com.alibaba.fastjson.JSON; import org.springframework.beans.BeanUtils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author visy.wang * @date 2024/6/27 21:55 */ public class Test { public static void main(String[] args) { List<Menu> menuList = new ArrayList<>(); //顺序可以任意调整,不影响结果 menuList.add(new Menu(1L, null, "菜单A", 1, 1,1)); menuList.add(new Menu(4L, 2L, "菜单BA", 2, 1,4)); menuList.add(new Menu(3L, 1L, "菜单AA", 2, 1,3)); menuList.add(new Menu(5L, 3L, "菜单AAA", 3, 1,5)); menuList.add(new Menu(2L, null, "菜单B", 1, 1,2)); //勾选的菜单ID集合 Set<Long> checkedMenuIds = new HashSet<>(); checkedMenuIds.add(3L); checkedMenuIds.add(5L); //map的方式+接口实现 List<TreeNode<Long>> treeNodeList = TreeUtil.list2tree( menuList, //原始列表 null, //根节点ID,用于提取顶层节点 menu -> { //将列表中的原始对象 MenuNodeV2 node = new MenuNodeV2();//创建一个节点 BeanUtils.copyProperties(menu, node);//复制原始对象的字段到节点对象 node.setIsCheck(checkedMenuIds.contains(menu.getId()) ? 1 : 0);//单独设置其他字段 return node;//返回节点对象 }, MenuNodeV2::new ); System.out.println(JSON.toJSONString(treeNodeList)); //递归的方式+接口实现 List<TreeNode<Long>> treeNodeList2 = TreeUtil.list2tree( menuList, //原始列表 null, //根节点ID,用于提取顶层节点 menu -> { //将列表中的原始对象 MenuNodeV2 node = new MenuNodeV2();//创建一个节点 BeanUtils.copyProperties(menu, node);//复制原始对象的字段到节点对象 node.setIsCheck(checkedMenuIds.contains(menu.getId()) ? 1 : 0);//单独设置其他字段 return node;//返回节点对象 } ); System.out.println(JSON.toJSONString(treeNodeList2)); } }
map的方式+接口实现
[ { "children": [ { "children": [ { "fid": 3, "id": 5, "isCheck": 1, "level": 3, "name": "菜单AAA", "parentId": 3, "status": 1, "weight": 5 } ], "fid": 1, "id": 3, "isCheck": 1, "level": 2, "name": "菜单AA", "parentId": 1, "status": 1, "weight": 3 } ], "id": 1, "isCheck": 0, "level": 1, "name": "菜单A", "status": 1, "weight": 1 }, { "children": [ { "fid": 2, "id": 4, "isCheck": 0, "level": 2, "name": "菜单BA", "parentId": 2, "status": 1, "weight": 4 } ], "id": 2, "isCheck": 0, "level": 1, "name": "菜单B", "status": 1, "weight": 2 } ]
递归的方式+接口实现
[ { "children": [ { "children": [ { "fid": 3, "id": 5, "isCheck": 1, "level": 3, "name": "菜单AAA", "parentId": 3, "status": 1, "weight": 5 } ], "fid": 1, "id": 3, "isCheck": 1, "level": 2, "name": "菜单AA", "parentId": 1, "status": 1, "weight": 3 } ], "id": 1, "isCheck": 0, "level": 1, "name": "菜单A", "status": 1, "weight": 1 }, { "children": [ { "fid": 2, "id": 4, "isCheck": 0, "level": 2, "name": "菜单BA", "parentId": 2, "status": 1, "weight": 4 } ], "id": 2, "isCheck": 0, "level": 1, "name": "菜单B", "status": 1, "weight": 2 } ]
public static <T,K,R> List<R> list2tree(List<T> list, K rootId, Function<T,K> idGetter, Function<T,K> pidGetter, Function<T,R> converter, BiConsumer<R,List<R>> childrenSetter){ if(Objects.isNull(list) || list.isEmpty()){ return null; } List<T> childList = new ArrayList<>(), surplusList = new ArrayList<>(); for (T t : list) { if(Objects.equals(rootId, pidGetter.apply(t))){ childList.add(t); }else{ surplusList.add(t); } } if(childList.isEmpty()){ return null; } return childList.stream().map(t -> { K id = idGetter.apply(t); R node = converter.apply(t); List<R> children = list2tree(surplusList, id, idGetter, pidGetter, converter, childrenSetter); childrenSetter.accept(node, children); return node; }).collect(Collectors.toList()); }
public static <T,K> List<TreeNode<K>> list2tree(List<T> list, K rootId, Function<T,TreeNode<K>> converter){ if(Objects.isNull(list) || list.isEmpty()){ return null; } List<T> surplusList = new ArrayList<>();//剩余列表(将已查找到的节点排除) List<TreeNode<K>> childList = new ArrayList<>();//rootId下的子节点列表 for (T t : list) { TreeNode<K> node = converter.apply(t); if(Objects.equals(rootId, node.getParentId())){ childList.add(node); }else{ surplusList.add(t); } } childList.forEach(node -> { node.setChildren(list2tree(surplusList, node.getId(), converter)); }); return childList.isEmpty() ? null : childList; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。