前言
该文章主要续上一篇文章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与对象引用的特性。