TreeUtil

凛冬雪夜發表於2024-11-09
點選檢視程式碼
 
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * List -> TreeList
 */
public class TreeUtil<T, K> {

    /**
     * 普通的非樹形 list
     */
    private List<T> list;
    /**
     * 樹形 list
     */
    private List<T> treeList;
    /**
     * 節點的 id 的 getter 方法
     */
    private Function<T, K> idGetterFunction;
    /**
     * 節點的父 id 的 getter 方法
     */
    private Function<T, K> parentIdGetterFunction;
    /**
     * 節點的子集合的 setter 方法
     */
    private BiConsumer<T, List<T>> childListSetterConsumer;
    /**
     * 節點的子集合的 getter 方法
     */
    private Function<T, List<T>> childListGetterFunction;
    /**
     * 節點的層數的 setter 方法(根節點層數為 1,次根節點的層數為 2,以此類推)
     */
    private FloorConsumer<T, Integer> floorSetterConsumer;

    /**
     * 構造方法(不設定層數)
     */
    public TreeUtil(List<T> list,
                    Function<T, K> idGetterFunction,
                    Function<T, K> parentIdGetterFunction,
                    BiConsumer<T, List<T>> childListSetterConsumer) {
        this.list = list;
        this.idGetterFunction = idGetterFunction;
        this.parentIdGetterFunction = parentIdGetterFunction;
        this.childListSetterConsumer = childListSetterConsumer;
    }

    /**
     * 構造方法(設定層數)
     */
    public TreeUtil(List<T> list,
                    Function<T, K> idGetterFunction,
                    Function<T, K> parentIdGetterFunction,
                    BiConsumer<T, List<T>> childListSetterConsumer,
                    FloorConsumer<T, Integer> floorSetterConsumer) {
        this.list = list;
        this.idGetterFunction = idGetterFunction;
        this.parentIdGetterFunction = parentIdGetterFunction;
        this.childListSetterConsumer = childListSetterConsumer;
        this.floorSetterConsumer = floorSetterConsumer;
    }

    /**
     * 構造方法
     */
    public TreeUtil(List<T> treeList, Function<T, List<T>> childListGetterFunction) {
        this.treeList = treeList;
        this.childListGetterFunction = childListGetterFunction;
    }

    /**
     * 普通 list -> 樹形 list
     */
    public List<T> fromListToTreeList() {
        if (list.isEmpty()) {
            return new ArrayList<>();
        }
        // 根節點集合
        List<T> rootList = new ArrayList<>();
        AtomicInteger atomicInteger = new AtomicInteger(1);
        for (T node : list) {
            // 若每個節點的 id 都不是這個節點的父id,則這個節點是根節點
            if (list.stream().noneMatch(i -> idGetterFunction.apply(i).equals(parentIdGetterFunction.apply(node)))) {
                rootList.add(buildTreeOfTheNode(node, 1, atomicInteger));
            }
        }
        return rootList;
    }

    /**
     * 樹形 list -> 普通 list
     */
    public List<T> fromTreeListToList() {
        List<T> list = new ArrayList<>();
        if (treeList.isEmpty()) {
            return list;
        }
        // 新增根節點
        list.addAll(treeList);
        // 某個層數的所有節點
        List<T> listOfAFloor = new CopyOnWriteArrayList<>(treeList); // listOfAFloor: while 迴圈的入參
        while (true) {
            if (CollectionUtils.isEmpty(listOfAFloor)) {
                break;
            }
            // 某個層數的所有節點的子節點(不包括孫子節點)
            List<T> childListOfAFloor = new CopyOnWriteArrayList<>();
            for (T t : listOfAFloor) {
                List<T> childList = childListGetterFunction.apply(t);
                if (CollectionUtils.isNotEmpty(childList)) {
                    list.addAll(childList);
                    childListOfAFloor.addAll(childList);
                }
            }
            listOfAFloor = childListOfAFloor; // listOfAFloor: 賦值為 childListOfAFloor,作為下一個 while 迴圈的入參
        }
        return list;
    }

    /**
     * 透過遞迴,將某個節點和他的子,孫,曾孫 ... 節點構建為樹形結構
     *
     * @param node  節點
     * @param floor 層數
     * @return 樹形結構
     */
    private T buildTreeOfTheNode(T node, Integer floor, AtomicInteger atomicInteger) {
        int count = atomicInteger.getAndIncrement();
        if (count > 500) {
            throw new RuntimeException("遞迴次數過多");
        }
        List<T> childList = new ArrayList<>();
        if (floorSetterConsumer != null) {
            floorSetterConsumer.setNodeFloor(node, floor);
        }
        for (T t : list) {
            if (idGetterFunction.apply(node).equals(parentIdGetterFunction.apply(t))) {
                childList.add(buildTreeOfTheNode(t, (floor + 1), atomicInteger));
            }
        }
        childListSetterConsumer.accept(node, childList);
        return node;
    }

}

點選檢視程式碼

@Data
class Person {
    /**
     * id
     */
    private Integer id;
    /**
     * 父id
     */
    private Integer pId;
    /**
     * 名稱
     */
    private String name;
    /**
     * 子節點集合
     */
    private List<Person> sons;
    /**
     * 層數
     */
    private Integer floor;
}

點選檢視程式碼

import java.util.ArrayList;
import java.util.List;

public class MainTest {

    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();

        Person grandFather = new Person();
        grandFather.setId(1);
        grandFather.setPId(0);
        grandFather.setName("爺爺");
        list.add(grandFather);

        Person father = new Person();
        father.setId(2);
        father.setPId(1);
        father.setName("爸爸");
        list.add(father);

        Person uncle_1 = new Person();
        uncle_1.setId(3);
        uncle_1.setPId(1);
        uncle_1.setName("大叔");
        list.add(uncle_1);

        Person uncle_2 = new Person();
        uncle_2.setId(4);
        uncle_2.setPId(1);
        uncle_2.setName("二叔");
        list.add(uncle_2);

        Person grandson = new Person();
        grandson.setId(5);
        grandson.setPId(2);
        grandson.setName("小明");
        list.add(grandson);

        //TreeUtil<Node, Integer> treeUtil = new TreeUtil<>(list, Node::getId, Node::getPId, Node::setSons);
        TreeUtil<Person, Integer> treeUtil = new TreeUtil<>(list, Person::getId, Person::getPId, Person::setSons, Person::setFloor);
        List<Person> treeList = treeUtil.fromListToTreeList();
        System.out.println(treeList);

        TreeUtil<Person, Integer> unBuildTreeUtil = new TreeUtil<>(treeList, Person::getSons);
        List<Person> personList = unBuildTreeUtil.fromTreeListToList();
        System.out.println(personList);

    }

}