mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-24 18:04:54 +08:00
feat(HierarchyUtil): 添加用于处理层级结构的通用工具
This commit is contained in:
parent
d25421bafd
commit
573d65d02b
@ -0,0 +1,221 @@
|
||||
package org.dromara.hutool.core.lang.hierarchy;
|
||||
|
||||
import org.dromara.hutool.core.lang.mutable.Mutable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 用于遍历层级结构的迭代器,提供了遍历过程中的回调方法:
|
||||
* <ul>
|
||||
* <li>{@link #isBreak}: 是否中断遍历,其在{@link #nextHierarchies}之前调用;</li>
|
||||
* <li>{@link #nextHierarchies}: 获得下一需要遍历的层级,当为空时结束;</li>
|
||||
* <li>{@link #getResult}: 当遍历完成后,获取迭代器结果;</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>默认提供了三类{@link HierarchyIterator}实现:
|
||||
* <ul>
|
||||
* <li>scan: 遍历所有的层级结构;</li>
|
||||
* <li>collector: 收集层级结构中所有符合条件的结果,并在结束遍历后返回所有结果;</li>
|
||||
* <li>find: 找到层级结构中符合条件的结果后立刻中断遍历,并返回该结果;</li>
|
||||
* </ul>
|
||||
* 可以实现自定义的{@link HierarchyIterator}来支持更多的遍历模式。
|
||||
*
|
||||
* @param <H> 层级类型
|
||||
* @param <R> 结果类型
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface HierarchyIterator<H, R> {
|
||||
|
||||
/**
|
||||
* 获取下一层级
|
||||
*
|
||||
* @param result 结果
|
||||
* @param hierarchy 当前层级
|
||||
* @return 向容器中添加元素的方法
|
||||
*/
|
||||
Collection<H> nextHierarchies(R result, H hierarchy);
|
||||
|
||||
/**
|
||||
* 是否中断遍历
|
||||
*
|
||||
* @param hierarchy 当前层级
|
||||
* @return 是否中断遍历
|
||||
*/
|
||||
default boolean isBreak(H hierarchy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结果
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
default R getResult() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 当{@code finder}返回非空时, 迭代器立刻中断, 返回结果
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param finder 查找器
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H, R> HierarchyIterator<H, R> find(
|
||||
final Function<H, Collection<H>> function, final Function<H, R> finder) {
|
||||
Objects.requireNonNull(function);
|
||||
Objects.requireNonNull(finder);
|
||||
final Mutable<R> mutable = Mutable.of(null);
|
||||
final Predicate<H> terminator = h -> {
|
||||
R r = finder.apply(h);
|
||||
if (r != null) {
|
||||
mutable.set(r);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return new HierarchyIteratorImpl<>(mutable::get, terminator, (r, h) -> function.apply(h));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 迭代器结果总是为{@code null}
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param terminator 是否终止遍历
|
||||
* @param <H> 层级结构类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H> HierarchyIterator<H, Void> scan(
|
||||
final Function<H, Collection<H>> function, final Predicate<H> terminator) {
|
||||
Objects.requireNonNull(function);
|
||||
return new HierarchyIteratorImpl<>(() -> null, terminator, (r, h) -> function.apply(h));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 迭代器结果总是为{@code null}
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param <H> 层级结构类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H> HierarchyIterator<H, Void> scan(final Function<H, Collection<H>> function) {
|
||||
return scan(function, h -> false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 若{@code mapper}返回非空, 则将结果添加到集合中,最终返回集合
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param collFactory 集合工厂
|
||||
* @param mapper 迭代器结果映射函数
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @param <C> 集合类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H, R, C extends Collection<R>> HierarchyIterator<H, C> collect(
|
||||
final Function<H, Collection<H>> function, final Supplier<C> collFactory, final Function<H, R> mapper) {
|
||||
Objects.requireNonNull(function);
|
||||
Objects.requireNonNull(collFactory);
|
||||
Objects.requireNonNull(mapper);
|
||||
final C collection = collFactory.get();
|
||||
return new HierarchyIteratorImpl<>(() -> collection, h -> false, (r, h) -> {
|
||||
final R apply = mapper.apply(h);
|
||||
if (Objects.nonNull(apply)) {
|
||||
collection.add(apply);
|
||||
}
|
||||
return function.apply(h);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 若{@code mapper}返回非空, 则将结果添加到集合中,最终返回集合
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param mapper 迭代器结果映射函数
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H, R> HierarchyIterator<H, List<R>> collect(
|
||||
final Function<H, Collection<H>> function, final Function<H, R> mapper) {
|
||||
return collect(function, ArrayList::new, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个{@link HierarchyIterator}对象, 则将非空结果添加到集合中,最终返回集合
|
||||
*
|
||||
* @param function 迭代器处理函数
|
||||
* @param <H> 层级结构类型
|
||||
* @return {@link HierarchyIterator}
|
||||
*/
|
||||
static <H> HierarchyIterator<H, List<H>> collect(
|
||||
final Function<H, Collection<H>> function) {
|
||||
return collect(function, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HierarchyIterator}的基本实现。
|
||||
*
|
||||
* @param <H> 层级类型
|
||||
* @param <R> 结果类型
|
||||
*/
|
||||
class HierarchyIteratorImpl<H, R> implements HierarchyIterator<H, R> {
|
||||
|
||||
private final Supplier<? extends R> resultFactory;
|
||||
private final Predicate<? super H> hierarchyFilter;
|
||||
private final BiFunction<? super R, ? super H, ? extends Collection<H>> hierarchyFinder;
|
||||
|
||||
public HierarchyIteratorImpl(
|
||||
final Supplier<? extends R> resultFactory, final Predicate<? super H> hierarchyFilter,
|
||||
final BiFunction<? super R, ? super H, ? extends Collection<H>> hierarchyFinder) {
|
||||
this.resultFactory = resultFactory;
|
||||
this.hierarchyFilter = hierarchyFilter;
|
||||
this.hierarchyFinder = hierarchyFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一层级
|
||||
*
|
||||
* @param result 结果
|
||||
* @param hierarchy 当前层级
|
||||
* @return 向容器中添加元素的方法
|
||||
*/
|
||||
@Override
|
||||
public Collection<H> nextHierarchies(final R result, final H hierarchy) {
|
||||
return hierarchyFinder.apply(result, hierarchy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否中断遍历
|
||||
*
|
||||
* @param hierarchy 当前层级
|
||||
* @return 是否中断遍历
|
||||
*/
|
||||
@Override
|
||||
public boolean isBreak(final H hierarchy) {
|
||||
return hierarchyFilter.test(hierarchy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结果
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public R getResult() {
|
||||
return resultFactory.get();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package org.dromara.hutool.core.lang.hierarchy;
|
||||
|
||||
import org.dromara.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* <p>通用层级结构扫描器工具类。<br>
|
||||
*
|
||||
* <p>用于根据广度优先或深度优先或搜索遍历如树或图等特定数据结构的层级,如:文件夹层级、类层级、方法或注解层级等。
|
||||
* 使用时指定遍历的起始节点,以及遍历算法,并配合{@link HierarchyIterator}即可完成遍历中的业务逻辑处理。
|
||||
*
|
||||
* <p>比如,我们可以使用其来访问树结构:
|
||||
* <pre>{@code
|
||||
* // 构建一个树形结构
|
||||
* Node root = Tree.build();
|
||||
* // 从树结构中通过深度优先查找某个节点
|
||||
* Node target = HierarchyUtil.traverseByDepthFirst(
|
||||
* root, HierarchyUtil.finder(Node::getChildren, node -> Objects.equals(node.getId(), "target") ? node : null)
|
||||
* );
|
||||
* // 从树结构中通过广度优先获取其所有的子节点
|
||||
* List<Node> nodes = HierarchyUtil.traverseByBreadthFirst(
|
||||
* root, HierarchyUtil.collector(Node::getChildren)
|
||||
* );
|
||||
* }</pre>
|
||||
*
|
||||
* @author huangchengxing
|
||||
* @see HierarchyIterator
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class HierarchyUtil {
|
||||
|
||||
// ================ breadth first ================
|
||||
|
||||
/**
|
||||
* 按广度优先遍历指定包括{@code hierarchy}本身在内层级结构
|
||||
*
|
||||
* @param hierarchy 层级结构
|
||||
* @param filter 过滤器,校验不通过的层级结构不会被查找
|
||||
* @param hierarchyIterator 遍历过程中的迭代器, 若迭代器返回空, 则不会继续遍历下一层级
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return 迭代器结果
|
||||
*/
|
||||
public static <H, R> R traverseByBreadthFirst(
|
||||
final H hierarchy, final Predicate<? super H> filter, final HierarchyIterator<H, R> hierarchyIterator) {
|
||||
return scanHierarchies(hierarchy, filter, hierarchyIterator, Collection::addAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按广度优先遍历指定包括{@code hierarchy}本身在内层级结构
|
||||
*
|
||||
* @param hierarchy 层级结构
|
||||
* @param hierarchyIterator 遍历过程中的迭代器, 若迭代器返回空, 则不会继续遍历下一层级
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return 迭代器结果
|
||||
*/
|
||||
public static <H, R> R traverseByBreadthFirst(final H hierarchy, final HierarchyIterator<H, R> hierarchyIterator) {
|
||||
return traverseByBreadthFirst(hierarchy, h -> true, hierarchyIterator);
|
||||
}
|
||||
|
||||
// ================ depth first ================
|
||||
|
||||
/**
|
||||
* 按深度优先遍历指定包括{@code hierarchy}本身在内层级结构
|
||||
*
|
||||
* @param hierarchy 层级结构
|
||||
* @param filter 过滤器,校验不通过的层级结构不会被查找
|
||||
* @param hierarchyIterator 遍历过程中的迭代器, 若迭代器返回空, 则不会继续遍历下一层级
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return 迭代器结果
|
||||
*/
|
||||
public static <H, R> R traverseByDepthFirst(
|
||||
final H hierarchy, final Predicate<? super H> filter, final HierarchyIterator<H, R> hierarchyIterator) {
|
||||
return scanHierarchies(hierarchy, filter, hierarchyIterator, (queue, hierarchies) -> {
|
||||
// 跳跃式添加,保证顺序
|
||||
int index = 0;
|
||||
for (final H h : hierarchies) {
|
||||
queue.add(index++, h);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按深度优先遍历指定包括{@code hierarchy}本身在内层级结构
|
||||
*
|
||||
* @param hierarchy 层级结构
|
||||
* @param hierarchyIterator 遍历过程中的迭代器, 若迭代器返回空, 则不会继续遍历下一层级
|
||||
* @param <H> 层级结构类型
|
||||
* @param <R> 迭代器结果类型
|
||||
* @return 迭代器结果
|
||||
*/
|
||||
public static <H, R> R traverseByDepthFirst(final H hierarchy, final HierarchyIterator<H, R> hierarchyIterator) {
|
||||
return traverseByDepthFirst(hierarchy, h -> true, hierarchyIterator);
|
||||
}
|
||||
|
||||
private static <H, R> R scanHierarchies(
|
||||
final H hierarchy, final Predicate<? super H> filter, final HierarchyIterator<H, R> hierarchyIterator,
|
||||
final BiConsumer<List<H>, Collection<? extends H>> appender) {
|
||||
Objects.requireNonNull(hierarchy);
|
||||
Objects.requireNonNull(filter);
|
||||
Objects.requireNonNull(hierarchyIterator);
|
||||
|
||||
// 遍历层级结构
|
||||
final Predicate<? super H> hierarchyFilter = filter.negate();
|
||||
final LinkedList<H> queue = new LinkedList<>();
|
||||
final Set<H> accessed = new HashSet<>();
|
||||
queue.add(hierarchy);
|
||||
while (!queue.isEmpty()) {
|
||||
// 跳过已经访问过或者被过滤的层级结构
|
||||
final H curr = queue.removeFirst();
|
||||
if (accessed.contains(curr) || hierarchyFilter.test(curr)) {
|
||||
continue;
|
||||
}
|
||||
accessed.add(curr);
|
||||
|
||||
// 若迭代器返回true,则结束遍历
|
||||
if (hierarchyIterator.isBreak(curr)) {
|
||||
return hierarchyIterator.getResult();
|
||||
}
|
||||
|
||||
// 获取下一需要遍历的层级
|
||||
final Collection<? extends H> nextHierarchies = hierarchyIterator.nextHierarchies(hierarchyIterator.getResult(), curr);
|
||||
if (CollUtil.isEmpty(nextHierarchies)) {
|
||||
continue;
|
||||
}
|
||||
appender.accept(queue, nextHierarchies);
|
||||
}
|
||||
return hierarchyIterator.getResult();
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
package org.dromara.hutool.core.lang.hierarchy;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* test for {@link HierarchyUtil}
|
||||
*
|
||||
* @author huangchengxing
|
||||
*/
|
||||
class HierarchyUtilTest {
|
||||
|
||||
private Map<String, String> tree;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
tree = new LinkedHashMap<>();
|
||||
|
||||
// 根节点
|
||||
tree.put("0", null);
|
||||
|
||||
// 第一层
|
||||
tree.put("0-1", "0");
|
||||
tree.put("0-2", "0");
|
||||
tree.put("0-3", "0");
|
||||
|
||||
// 第三层
|
||||
tree.put("0-1-1", "0-1");
|
||||
tree.put("0-1-2", "0-1");
|
||||
tree.put("0-1-3", "0-1");
|
||||
|
||||
tree.put("0-2-1", "0-2");
|
||||
tree.put("0-2-2", "0-2");
|
||||
tree.put("0-2-3", "0-2");
|
||||
|
||||
tree.put("0-3-1", "0-3");
|
||||
tree.put("0-3-2", "0-3");
|
||||
tree.put("0-3-3", "0-3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTraverseByBreadthFirst() {
|
||||
// 按广度优先遍历所有节点
|
||||
Set<String> nodes = new LinkedHashSet<>();
|
||||
HierarchyUtil.traverseByBreadthFirst("0", HierarchyIterator.scan(t -> {
|
||||
nodes.add(t);
|
||||
return tree.entrySet().stream()
|
||||
.filter(e -> Objects.equals(t, e.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}));
|
||||
Assertions.assertEquals(13, nodes.size());
|
||||
Assertions.assertEquals(
|
||||
new LinkedHashSet<>(Arrays.asList("0", "0-1", "0-2", "0-3", "0-1-1", "0-1-2", "0-1-3", "0-2-1", "0-2-2", "0-2-3", "0-3-1", "0-3-2", "0-3-3")),
|
||||
nodes
|
||||
);
|
||||
|
||||
// 按广度优先寻找 0-2-3
|
||||
String target = HierarchyUtil.traverseByBreadthFirst("0", HierarchyIterator.find(parentFinder(),
|
||||
t -> Objects.equals(t, "0-2-3") ? t : null
|
||||
));
|
||||
Assertions.assertEquals("0-2-3", target);
|
||||
|
||||
// 按广度优先获取 0-2 的所有子节点
|
||||
List<String> children = HierarchyUtil.traverseByBreadthFirst(
|
||||
"0", HierarchyIterator.collect(parentFinder(),
|
||||
t -> Objects.equals(tree.get(t), "0-2") ? t : null
|
||||
)
|
||||
);
|
||||
Assertions.assertEquals(3, children.size());
|
||||
Assertions.assertEquals(new ArrayList<>(Arrays.asList("0-2-1", "0-2-2", "0-2-3")), children);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTraverseByDepthFirst() {
|
||||
// 按深度优先遍历所有节点
|
||||
Set<String> nodes = new LinkedHashSet<>();
|
||||
HierarchyUtil.traverseByDepthFirst("0", HierarchyIterator.scan(t -> {
|
||||
nodes.add(t);
|
||||
return tree.entrySet().stream()
|
||||
.filter(e -> Objects.equals(t, e.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}));
|
||||
Assertions.assertEquals(13, nodes.size());
|
||||
Assertions.assertEquals(
|
||||
new LinkedHashSet<>(Arrays.asList("0", "0-1", "0-1-1", "0-1-2", "0-1-3", "0-2", "0-2-1", "0-2-2", "0-2-3", "0-3", "0-3-1", "0-3-2", "0-3-3")),
|
||||
nodes
|
||||
);
|
||||
|
||||
// 按深度优先寻找 0-2-3
|
||||
String target = HierarchyUtil.traverseByDepthFirst("0", HierarchyIterator.find(parentFinder(),
|
||||
t -> Objects.equals(t, "0-2-3") ? t : null
|
||||
));
|
||||
Assertions.assertEquals("0-2-3", target);
|
||||
|
||||
// 按深度优先获取 0-2 的所有子节点
|
||||
List<String> children = HierarchyUtil.traverseByDepthFirst(
|
||||
"0", HierarchyIterator.collect(parentFinder(),
|
||||
t -> Objects.equals(tree.get(t), "0-2") ? t : null
|
||||
)
|
||||
);
|
||||
Assertions.assertEquals(3, children.size());
|
||||
Assertions.assertEquals(new ArrayList<>(Arrays.asList("0-2-1", "0-2-2", "0-2-3")), children);
|
||||
}
|
||||
|
||||
private Function<String, Collection<String>> parentFinder() {
|
||||
return t -> tree.entrySet()
|
||||
.stream()
|
||||
.filter(e -> Objects.equals(t, e.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user