java 處理樹形結構工具類


<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 = []
}]

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM