Java利用遞歸實現樹形結構的工具類

需求描述

有時候,我們的數據是帶有層級的,比如常見的省市區三級聯動,就是一層套著一層,如下圖:

而我們在數據庫存放數據的時候,往往是列表形式的,如下圖:

那麼當我們從數據庫查詢出來,返回給前端的時候,前端又需要給出樹形層級的時候,這個時候可能就需要遞歸處理為樹形結構瞭,因此下面這個工具或許就可以用得上瞭。

使用示例

我們按照上面定義一個Place對象,打上工具註解:

  • @TreeKey 標識唯一
  • @TreeParentKey 標識父節點標識
  • @TreeChildren 標識子孫節點集合
@Data
@Data
public class Place {

    @TreeKey
    private String id;

    @TreeParentKey
    private String parentId;

    private String name;

    @TreeChildren
    private List<Place> children;

    public Place(String id, String name, String parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }
}

測試:

public class Test {

    public static void main(String[] args) {
        List<Place> places = new ArrayList<>();
        places.add(new Place("510000", "四川省", "0"));
        places.add(new Place("510100", "成都市", "510000"));
        places.add(new Place("510107", "武侯區", "510100"));
        places.add(new Place("510116", "雙流區", "510100"));
        places.add(new Place("511600", "廣安市", "510000"));
        places.add(new Place("511603", "前鋒區", "511600"));
        places.add(new Place("511621", "嶽池縣", "511600"));
        List<Place> treeList = TreeUtils.getTree(places, "0");
        System.out.println(JSON.toJSONString(treeList));
    }

}

最終效果:

工具代碼

@TreeKey

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeKey {
}

@TreeParentKey

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeParentKey {
}

@TreeChildren

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeChildren {
}

@TreeUtils

package com.csd.utils.tree;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 遞歸求樹形工具類
 *
 * @author Yuanqiang.Zhang
 * @since 2023/3/8
 */
public class TreeUtils {

    /**
     * 集合轉化為樹形
     *
     * @param list             集合
     * @param highestParentKey 最高層父節點值
     * @param <T>              泛型
     * @return 樹形
     */
    public static <T> List<T> getTree(List<T> list, Object highestParentKey) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return Collections.emptyList();
        }
        Field key = null;
        Field parentKey = null;
        Field children = null;
        Field[] fields = list.get(0).getClass().getDeclaredFields();
        for (Field field : fields) {
            if (Objects.isNull(key)) {
                TreeKey treeKey = field.getAnnotation(TreeKey.class);
                if (Objects.nonNull(treeKey)) {
                    key = field;
                    continue;
                }
            }
            if (Objects.isNull(parentKey)) {
                TreeParentKey treeParentKey = field.getAnnotation(TreeParentKey.class);
                if (Objects.nonNull(treeParentKey)) {
                    parentKey = field;
                    continue;
                }
            }
            if (Objects.isNull(children)) {
                TreeChildren treeChildren = field.getAnnotation(TreeChildren.class);
                if (Objects.nonNull(treeChildren)) {
                    children = field;
                    continue;
                }
            }
        }
        if (Objects.isNull(key) || Objects.isNull(parentKey) || Objects.isNull(children)) {
            return Collections.emptyList();
        }
        key.setAccessible(true);
        parentKey.setAccessible(true);
        children.setAccessible(true);
        // 獲取最高層數據
        List<T> highs = new ArrayList<>();
        try {
            for (T t : list) {
                Object pk = parentKey.get(t);
                if (getString(pk).equals(getString(highestParentKey))) {
                    highs.add(t);
                }
            }
            // 獲取最高層子孫節點
            for (T t : highs) {
                setChildren(list, t, key, parentKey, children);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return highs;
    }

    /**
     * 獲取子孫節點
     *
     * @param list      集合
     * @param parent    父節點對象
     * @param key       唯一屬性
     * @param parentKey 父唯一屬性
     * @param children  節點
     * @param <T>       泛型
     * @return 帶有子孫集合的父節點對象
     * @throws IllegalAccessException
     */
    private static <T> T setChildren(List<T> list, T parent, Field key, Field parentKey, Field children) throws IllegalAccessException {
        Object k = key.get(parent);
        List<T> tempList = new ArrayList<>();
        for (T t : list) {
            Object pk = parentKey.get(t);
            if (getString(k).equals(getString(pk))) {
                tempList.add(setChildren(list, t, key, parentKey, children));
            }
        }
        children.set(parent, tempList);
        return parent;
    }

    /**
     * 獲取字符串
     *
     * @param o 值
     * @return 字符串
     */
    private static String getString(Object o) {
        return Objects.isNull(o) ? "" : o.toString();
    }

}

以上就是Java利用遞歸實現樹形結構的工具類的詳細內容,更多關於Java樹形結構的資料請關註WalkonNet其它相關文章!

推薦閱讀: