通过JDK函数式遍历列表设置父子关联(如菜单、分类等)

前言

该文章主要续上一篇文章JDK函数式编程的认知与使用中的例子补充,通过一个简洁的函数式方法将含父子关系的类列表(如分类、菜单等)进行树状排序且无需递归设置,当含需要从数据库中获取所有此类数据对象并树状排序业务时可使用该方法。

范例代码

Classify.java

@Data
import lombok.Data;

import java.util.List;

/**
 * Classify
 *
 * @author Wilson
 * @date 2020/2/5
 */
@Data
public class Classify {
    private Long id;
    private String name;
    private Integer level;
    private Long parentId;
    /**
     * 子分类列表
     */
    private transient List<Classify> sonClassifies;
}

工具方法及测试类TreeUtils:

package io.wilson.ssh.utils;

import com.google.common.collect.Lists;
import io.springframework.common.utils.ListUtils;
import io.wilson.ssh.Classify;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * TreeUtils
 *
 * @author Wilson
 * @date 2020/2/11
 */
public class TreeUtils {

    /**
     * 将含父子关系的类列表进行子列表属性设置并最高级的类列表(如分类树parentId为null时为一级分类,则取设置完子列表的parentId为null的分类列表)
     *
     * @param list           元素列表
     * @param idGetter       类唯一标识id getter function
     * @param parentIdGetter 类的父id标识 getter function
     * @param sonsGetter     类的子元素列表属性getter function
     * @param sonsSetter     类的子元素列表属性setter function
     * @param ancestorVal    最高级的parentId值
     * @param <K>            父子标识属性类型
     * @param <T>            对象类型
     * @return
     */
    public static <K, T> List<T> tree(List<T> list, Function<T, K> idGetter, Function<T, K> parentIdGetter,
                                      Function<T, List<T>> sonsGetter, BiConsumer<T, List<T>> sonsSetter, K ancestorVal) {
        // ① 将对象列表以对象id、对象转为映射
        Map<K, T> idMap = ListUtils.collectToMap(list, idGetter);
        // 最高级的元素列表映射, id最高级元素的父标识(parentId)值,value为最高级元素列表
        Map<K, List<T>> ancestorMap = new HashMap<>(1);
        // ②
        list.forEach(each -> {
            // 获取当前对象的父id
            K parentId = parentIdGetter.apply(each);
            // 父id为最高级的标识值则放入ancestorMap中
            if (Objects.equals(parentId, ancestorVal)) {
                ancestorMap.compute(ancestorVal,
                        (key, parentList) -> parentList == null ? Lists.newArrayList(each) : ListUtils.add(parentList, each));
            } else {
                // 获取当前对象的父关系对象
                T parent = idMap.get(parentId);
                // 获取当前对象的父关系对象下的子对象列表,并向列表中添加当前对象
                List<T> parentSons = sonsGetter.apply(parent);
                if (parentSons == null) {
                    sonsSetter.accept(parent, Lists.newArrayList(each));
                } else {
                    sonsGetter.apply(parent).add(each);
                }
            }
        });
        // 返回最高级元素列表
        return ancestorMap.get(ancestorVal);
    }

    public static void main(String[] args) {
        List<Classify> totalList = new ArrayList<>(16);
        totalList.add(buildClassify(1L, null, "classify-L1-A"));
        totalList.add(buildClassify(2L, null, "classify-L1-B"));
        totalList.add(buildClassify(3L, null, "classify-L1-C"));
        totalList.add(buildClassify(4L, null, "classify-L1-D"));
        totalList.add(buildClassify(11L, 1L, "classify-L2-parent1-A"));
        totalList.add(buildClassify(12L, 1L, "classify-L2-parent1-B"));
        totalList.add(buildClassify(13L, 1L, "classify-L2-parent1-C"));
        totalList.add(buildClassify(21L, 2L, "classify-L2-parent2-A"));
        totalList.add(buildClassify(22L, 2L, "classify-L2-parent2-B"));
        totalList.add(buildClassify(41L, 4L, "classify-L2-parent4-A"));
        totalList.add(buildClassify(42L, 4L, "classify-L2-parent4-B"));
        totalList.add(buildClassify(211L, 21L, "classify-L3-parent21-A"));
        totalList.add(buildClassify(212L, 21L, "classify-L3-parent21-B"));
        List<Classify> levelOneList = tree(totalList, Classify::getId, Classify::getParentId, Classify::getSonClassifies,
                Classify::setSonClassifies, null);
        levelOneList.forEach(System.out::println);
    }

    private static Classify buildClassify(Long id, Long parentId, String name) {
        Classify classify = new Classify();
        classify.setId(id);
        classify.setName(name);
        classify.setParentId(parentId);
        return classify;
    }

}

运行结果如下:

Classify(id=1, name=classify-L1-A, level=null, parentId=0, sonClassifies=[Classify(id=11, name=classify-L2-parent1-A, level=null, parentId=1, sonClassifies=null), Classify(id=12, name=classify-L2-parent1-B, level=null, parentId=1, sonClassifies=null), Classify(id=13, name=classify-L2-parent1-C, level=null, parentId=1, sonClassifies=null)])
Classify(id=2, name=classify-L1-B, level=null, parentId=0, sonClassifies=[Classify(id=21, name=classify-L2-parent2-A, level=null, parentId=2, sonClassifies=[Classify(id=211, name=classify-L3-parent21-A, level=null, parentId=21, sonClassifies=null), Classify(id=212, name=classify-L3-parent21-B, level=null, parentId=21, sonClassifies=null)]), Classify(id=22, name=classify-L2-parent2-B, level=null, parentId=2, sonClassifies=null)])
Classify(id=3, name=classify-L1-C, level=null, parentId=0, sonClassifies=null)
Classify(id=4, name=classify-L1-D, level=null, parentId=0, sonClassifies=[Classify(id=41, name=classify-L2-parent4-A, level=null, parentId=4, sonClassifies=null), Classify(id=42, name=classify-L2-parent4-B, level=null, parentId=4, sonClassifies=null)])

可以看出tree()方法已成功进行对分类列表进行子集设置,相比于递归设置,该方法无论列表里有多少个元素都只需进行2次遍历操作(方法中的①、②),源于充分利用了Map与对象引用的特性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值