一、场景
树结构
比如一级部门,二级部门,三级部门......形成一个树结构全部返回
二、代码
1.ITreeNode
public interface ITreeNode<T, S extends ITreeNode<T, S>> {
/**
* 获取节点id
*
* @return
*/
T id();
/**
* 获取该节点的父节点id
*
* @return
*/
T parentId();
/**
* 数量
*
* @return {@link Long}
*/
default Long count() {
return 0L;
}
/**
* 设置数量
*/
default void setCount(Long count) {
}
/**
* 是否是根节点
*
* @return
*/
boolean root();
/**
* 设置节点的子节点列表
*
* @param children
*/
void setChildrenNodes(List<S> children);
/**
* 获取所有子节点
*
* @return
*/
Iterable<S> getChildrenNodes();
}
2.TreeNodeUtils
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeNodeUtils {
public static <S, T extends ITreeNode<S, T>> List<T> generateTrees(List<T> nodes) {
return generateTrees(nodes, Boolean.FALSE);
}
public static <S, T extends ITreeNode<S, T>> List<T> generateTrees(List<T> nodes, boolean filterZeroCount) {
List<T> roots = new ArrayList<>();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (node.root()) {
roots.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
roots.forEach(r -> setChildren(r, nodes, filterZeroCount));
return roots;
}
/**
* 遍历所有节点,提供回调函数
*
* @param nodes 节点
* @param node 节点
*/
public static <S, T extends ITreeNode<S, T>> void traversalAllNode(Iterable<T> nodes, Consumer<T> node) {
nodes.forEach(n -> {
node.accept(n);
Iterable<T> childrenNodes = n.getChildrenNodes();
if (Objects.nonNull(childrenNodes)) {
boolean hasNext = childrenNodes.iterator().hasNext();
if (hasNext) {
traversalAllNode(childrenNodes, node);
}
}
}
);
}
public static <S, T extends ITreeNode<S, T>> void setChildren(T parent, List<T> nodes, boolean filterZeroCount) {
List<T> children = new ArrayList<>();
Object parentId = parent.id();
for (Iterator<T> ite = nodes.iterator(); ite.hasNext(); ) {
T node = ite.next();
if (Objects.isNull(node)) {
ite.remove();
continue;
}
if (Objects.equals(node.parentId(), parentId)) {
children.add(node);
// 从所有节点列表中删除该节点,以免后续重复遍历该节点
ite.remove();
}
}
// 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
if (children.isEmpty()) {
return;
}
children.forEach(m -> {
// 递归设置子节点
setChildren(m, nodes, filterZeroCount);
});
if (!CollectionUtils.isEmpty(children) && filterZeroCount) {
children.removeIf(zeroCntChild -> zeroCntChild.count() == 0);
}
parent.setChildrenNodes(children);
long sum = children.stream().map(T::count).mapToLong(value -> Objects.isNull(value) ? 0L : value).sum();
parent.setCount(sum);
}
public static <S, T extends ITreeNode<S, T>> List<T> getLeaves(T parent) {
List<T> leaves = new ArrayList<>();
fillLeaves(parent, leaves);
return leaves;
}
/**
* 将parent的所有叶子节点填充至leafs列表中
*
* @param parent 父节点
* @param leaves 叶子节点列表
* @param <T> 实际节点类型
*/
public static <S, T extends ITreeNode<S, T>> void fillLeaves(T parent, List<T> leaves) {
List<T> childrenNodes = (List<T>) parent.getChildrenNodes();
// 如果节点没有子节点则说明为叶子节点
if (CollectionUtils.isEmpty(childrenNodes)) {
leaves.add(parent);
return;
}
// 递归调用子节点,查找叶子节点
for (T childrenNode : childrenNodes) {
fillLeaves(childrenNode, leaves);
}
}
/**
* 过滤树
* @param trees
* @param filterNodes
* @param <S>
* @param <T>
* @return
*/
public static <S, T extends ITreeNode<S, T>> List<T> filterTrees(List<T> trees, List<S> filterNodes) {
List<T> filterTrees = new ArrayList<>();
for (S filterNode : filterNodes) {
// 通过克隆对象清除过滤点后层级,不动原对象,防止过滤点有层级关系
List<T> canBeModifiedTrees = new ArrayList<>(trees);
T filterTree = filterPoint(filterNode, canBeModifiedTrees);
if (!Objects.isNull(filterTree)) {
filterTrees.add(filterTree);
}
}
return filterTrees;
}
/**
* 过滤节点
*/
public static <S, T extends ITreeNode<S, T>> T filterPoint(S filterNode, List<T> childrenTrees) {
if(!CollectionUtils.isEmpty(childrenTrees)) {
for (ListIterator<T> iterator = childrenTrees.listIterator(); iterator.hasNext();) {
T node = iterator.next();
Object id = node.id();
T canBeModifiedNode = node.clone(node);
// 找到过滤点
if (id.equals(filterNode)) {
canBeModifiedNode.setChildrenNodes(new ArrayList<>());
return canBeModifiedNode;
}
if (node.getChildrenNodes() != null) {
List<T> childrenNodes = Lists.newArrayList((node.getChildrenNodes()));
// 过滤点向下递归
T findNode = filterPoint(filterNode, childrenNodes);
if (!Objects.isNull(findNode)) {
canBeModifiedNode.setChildrenNodes(Arrays.asList(findNode));
return canBeModifiedNode;
}
}
}
}
return null;
}
}
3.DepartmentTreeNode
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema
public class DepartmentTreeNode implements ITreeNode<Integer, MarsDepartmentTreeNode> {
@Schema(title = "部门ID", example = "10")
private Integer departmentId;
@Schema(title = "部门名", example = "产品研发中心")
private String departmentName;
@Schema(title = "部门父ID", example = "1")
private Integer parentDepartmentId;
@Schema(title = "子节点")
private List<MarsDepartmentTreeNode> childrenNodes;
@Override
public Integer id() {
return this.departmentId;
}
@Override
public Integer parentId() {
return this.parentDepartmentId;
}
@Override
public boolean root() {
return Objects.equals(-1, this.parentDepartmentId);
}
@Override
public void setChildrenNodes(List<MarsDepartmentTreeNode> childrenNodes) {
this.childrenNodes = childrenNodes;
}
}