Background
平常很多情况下会遇到表中包含id、父id这种具有层级关系的数据,查询出来一个List集合,有时候需要后端转换成完整的层级结构。这里提供一种不需要递归的实现思路,且复杂度为O(n)。
Features
- 支持泛型,任意具有层级结构的实体对象,只要包含id,父id,和子集合list的对象都可以使用该方法转换;
- 浅克隆,会影响原始数据List集合;
- 时间复杂度为n。
Code
/**
* 层级转换工具类
*
* @author 是豆本豆呀
* @date 2022-01-24 10:49:17
*/
public class LevelUtils<T, U> {
/**
* 层级结构转换
*
* @param data 层级数据
* @param getIdFunc 获取id的方法
* @param getPidFunc 获取父id的方法
* @param getChildrenFunc 获取子集合的方法
* @param setChildrenFunc 如果子集合为空,set子集合的方法
* @return java.util.List<T>
* @author 是豆本豆呀
* @date 2022-01-24 11:23:28
*/
public static <T, U> List<T> convert(List<T> data,
Function<T, U> getIdFunc,
Function<T, U> getPidFunc,
Function<T, List<T>> getChildrenFunc,
BiConsumer<T, List<T>> setChildrenFunc) {
if (data == null) {
return null;
}
Map<U, T> mapper = data.stream().collect(Collectors.toMap(getIdFunc, Function.identity()));
List<T> result = new ArrayList<>();
for (T element : data) {
T parent = mapper.get(getPidFunc.apply(element));
if (parent == null) {
result.add(element);
continue;
}
if (getChildrenFunc.apply(parent) == null) {
setChildrenFunc.accept(parent, new ArrayList<>());
}
getChildrenFunc.apply(parent).add(element);
}
return result;
}
public static void main(String[] args) {
@AllArgsConstructor
@Data
class Menu {
private Integer id;
private Integer pid;
private String name;
private List<Menu> children;
}
List<Menu> records = new ArrayList<>();
records.add(new Menu(1, 0, "第一章", null));
records.add(new Menu(2, 0, "第二章", null));
records.add(new Menu(3, 0, "第三章", null));
records.add(new Menu(21, 2, "第二章-第一节", null));
records.add(new Menu(211, 21, "第二章-第一节-第一段", null));
List<Menu> result = LevelUtils.convert(records, Menu::getId, Menu::getPid, Menu::getChildren, Menu::setChildren);
System.out.println(JSONUtil.toJsonStr(result));
}
}