<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
package com.ytkj.rose.util; import org.springframework.util.StringUtils; import javax.validation.constraints.NotNull; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; public class TreeUtils { /** * 集合轉樹結構 * * @param collection 目標集合 * @param clazz 集合元素類型 * @return 轉換后的樹形結構 */ public static <T> Collection<T> toTree(@NotNull Collection<T> collection, @NotNull Class<T> clazz) { return toTree(collection, null, null, null, clazz); } /** * 集合轉樹結構 * * @param collection 目標集合 * @param id 節點編號字段名稱 * @param parent 父節點編號字段名稱 * @param children 子節點集合屬性名稱 * @param clazz 集合元素類型 * @return 轉換后的樹形結構 */ public static <T> Collection<T> toTree(@NotNull Collection<T> collection, String id, String parent, String children, @NotNull Class<T> clazz) { try { if (collection == null || collection.isEmpty()) return null;// 如果目標集合為空,直接返回一個空樹 if (StringUtils.isEmpty(id)) id = "id"; // 如果被依賴字段名稱為空則默認為id if (StringUtils.isEmpty(parent)) parent = "parent"; // 如果依賴字段為空則默認為parent if (StringUtils.isEmpty(children)) children = "children"; // 如果子節點集合屬性名稱為空則默認為children // 初始化根節點集合, 支持 Set 和 List Collection<T> roots; if (collection.getClass().isAssignableFrom(Set.class)) { roots = new HashSet<>(); } else { roots = new ArrayList<>(); } // 獲取 id 字段, 從當前對象或其父類 Field idField; try { idField = clazz.getDeclaredField(id); } catch (NoSuchFieldException e1) { idField = clazz.getSuperclass().getDeclaredField(id); } // 獲取 parentId 字段, 從當前對象或其父類 Field parentField; try { parentField = clazz.getDeclaredField(parent); } catch (NoSuchFieldException e1) { parentField = clazz.getSuperclass().getDeclaredField(parent); } // 獲取 children 字段, 從當前對象或其父類 Field childrenField; try { childrenField = clazz.getDeclaredField(children); } catch (NoSuchFieldException e1) { childrenField = clazz.getSuperclass().getDeclaredField(children); } // 設置為可訪問 idField.setAccessible(true); parentField.setAccessible(true); childrenField.setAccessible(true); // 找出所有的根節點 for (T c : collection) { Object parentId = parentField.get(c); if (isRootNode(parentId)) { roots.add(c); } } // 從目標集合移除所有根節點 collection.removeAll(roots); // 遍歷根節點, 依次添加子節點 for (T root : roots) { addChild(root, collection, idField, parentField, childrenField); } // 關閉可訪問 idField.setAccessible(false); parentField.setAccessible(false); childrenField.setAccessible(false); return roots; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 為目標節點添加孩子節點 * * @param node 目標節點 * @param collection 目標集合 * @param idField ID 字段 * @param parentField 父節點字段 * @param childrenField 字節點字段 */ private static <T> void addChild(@NotNull T node, @NotNull Collection<T> collection, @NotNull Field idField, @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException { Object id = idField.get(node); Collection<T> children = (Collection<T>) childrenField.get(node); // 如果子節點的集合為 null, 初始化孩子集合 if (children == null) { if (collection.getClass().isAssignableFrom(Set.class)) { children = new HashSet<>(); } else children = new ArrayList<>(); } for (T t : collection) { Object o = parentField.get(t); if (id.equals(o)) { // 將當前節點添加到目標節點的孩子節點 children.add(t); // 重設目標節點的孩子節點集合,這里必須重設,因為如果目標節點的孩子節點是null的話,這樣是沒有地址的,就會造成數據丟失,所以必須重設,如果目標節點所在類的孩子節點初始化為一個空集合,而不是null,則可以不需要這一步,因為java一切皆指針 childrenField.set(node, children); // 遞歸添加孩子節點 addChild(t, collection, idField, parentField, childrenField); } } } /** * 判斷是否是根節點, 判斷方式為: 父節點編號為空或為 0, 則認為是根節點. 此處的判斷應根據自己的業務數據而定. * @param parentId 父節點編號 * @return 是否是根節點 */ private static boolean isRootNode(Object parentId) { boolean flag = false; if (parentId == null) { flag = true; } else if (parentId instanceof String && (StringUtils.isEmpty(parentId) || parentId.equals("0"))) { flag = true; } else if (parentId instanceof Integer && Integer.valueOf(0).equals(parentId)) { flag = true; } return flag; } }
測試:
List<TMenu> list = tMenuMapper.selectAll(); Collection<TMenu> tMenus = TreeUtils.toTree(list, "id", "pid", "children", TMenu.class); logger.debug("菜單={}",tMenus);
結果
[TMenu { id = 1, pid = 0, name = '控制面板', icon = 'glyphicon glyphicon-dashboard', url = 'main.html', children = [] }, TMenu { id = 2, pid = 0, name = '權限管理', icon = 'glyphicon glyphicon glyphicon-tasks', url = 'null', children = [TMenu { id = 3, pid = 2, name = '用戶維護', icon = 'glyphicon glyphicon-user', url = 'admin/index.html', children = [] }, TMenu { id = 4, pid = 2, name = '角色維護', icon = 'glyphicon glyphicon-king', url = 'role/index.html', children = [] }, TMenu { id = 5, pid = 2, name = '權限維護', icon = 'glyphicon glyphicon-lock', url = 'permission/index.html', children = [] }, TMenu { id = 6, pid = 2, name = '菜單維護', icon = 'glyphicon glyphicon-th-list', url = 'menu/index.html', children = [] }] }, TMenu { id = 7, pid = 0, name = '業務審核', icon = 'glyphicon glyphicon-ok', url = 'null', children = [TMenu { id = 8, pid = 7, name = '實名認證審核', icon = 'glyphicon glyphicon-check', url = 'auth_cert/index.html', children = [] }, TMenu { id = 9, pid = 7, name = '廣告審核', icon = 'glyphicon glyphicon-check', url = 'auth_adv/index.html', children = [] }, TMenu { id = 10, pid = 7, name = '項目審核', icon = 'glyphicon glyphicon-check', url = 'auth_project/index.html', children = [] }] }, TMenu { id = 11, pid = 0, name = '業務管理', icon = 'glyphicon glyphicon-th-large', url = 'null', children = [TMenu { id = 12, pid = 11, name = '資質維護', icon = 'glyphicon glyphicon-picture', url = 'cert/index.html', children = [] }, TMenu { id = 13, pid = 11, name = '分類管理', icon = 'glyphicon glyphicon-equalizer', url = 'certtype/index.html', children = [] }, TMenu { id = 14, pid = 11, name = '流程管理', icon = 'glyphicon glyphicon-random', url = 'process/index.html', children = [] }, TMenu { id = 15, pid = 11, name = '廣告管理', icon = 'glyphicon glyphicon-hdd', url = 'advert/index.html', children = [] }, TMenu { id = 16, pid = 11, name = '消息模板', icon = 'glyphicon glyphicon-comment', url = 'message/index.html', children = [] }, TMenu { id = 17, pid = 11, name = '項目分類', icon = 'glyphicon glyphicon-list', url = 'projectType/index.html', children = [] }, TMenu { id = 18, pid = 11, name = '項目標簽', icon = 'glyphicon glyphicon-tags', url = 'tag/index.html', children = [] }] }, TMenu { id = 19, pid = 0, name = '參數管理', icon = 'glyphicon glyphicon-list-alt', url = 'param/index.html', children = [] }]