From e96eff5cff0e99707dd703343744df789286dd2a Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Sat, 26 Nov 2022 22:05:10 +0800 Subject: [PATCH 01/18] =?UTF-8?q?:trollface:=20=E7=A7=BB=E9=99=A4EasyStrea?= =?UTF-8?q?m=E4=B8=AD=E5=AF=B9=E4=BA=8E=E6=A0=91=E7=9A=84=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/stream/CollectorUtil.java | 118 ----------------- .../core/stream/TerminableWrappedStream.java | 77 ----------- .../cn/hutool/core/stream/EasyStreamTest.java | 125 ------------------ 3 files changed, 320 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java index 71bdc4ff6..20732ff6d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java @@ -430,124 +430,6 @@ public class CollectorUtil { return toMap(Map.Entry::getKey, Map.Entry::getValue); } - /** - *

将集合转换为树,默认用 {@code parentId == null} 来判断树的根节点 - * 因为需要在当前传入数据里查找,所以这是一个结束操作
- * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} 会过滤掉id为null的元素 - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param childrenSetter children的setter对应的lambda,可以写作{ @code Student::setChildren} - * @param isParallel 是否并行去组装,数据量特别大时使用 - * @param 此处是元素类型 - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *

{@code
-	 * List studentTree = students.stream().collect(toTree(Student::getId, Student::getParentId, Student::setChildren, isParallel));
-	 * }
- */ - public static , T> Collector> toTree( - final Function idGetter, - final Function pIdGetter, - final BiConsumer> childrenSetter, - final boolean isParallel) { - return toTree(idGetter, pIdGetter, null, childrenSetter, isParallel); - } - - /** - *

将集合转换为树,默认用 {@code parentId == pidValue} 来判断树的根节点,可以为null - * 因为需要在当前传入数据里查找,所以这是一个结束操作
- * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} 会过滤掉id为null的元素 - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param pidValue pid的值 - * @param childrenSetter children的setter对应的lambda,可以写作{ @code Student::setChildren} - * @param isParallel 是否并行去组装,数据量特别大时使用 - * @param 此处是元素类型 - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *

{@code
-	 * List studentTree = students.stream().collect(toTree(Student::getId, Student::getParentId, 0L, Student::setChildren, isParallel));
-	 * }
- * @author VampireAchao - */ - public static , T> Collector> toTree( - final Function idGetter, - final Function pIdGetter, - final R pidValue, - final BiConsumer> childrenSetter, - final boolean isParallel) { - return Collectors.collectingAndThen(filtering(e -> idGetter.apply(e) != null, groupingBy(pIdGetter, Collectors.toList())), - getChildrenFromMapByPidAndSet(idGetter, pIdValuesMap -> pIdValuesMap.get(pidValue), childrenSetter, isParallel)); - } - - /** - * 将集合转换为树,自定义根节点的判断条件 - * 因为需要在当前传入数据里查找,所以这是一个结束操作 - * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} 会过滤掉id为null的元素 - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param childrenSetter children的setter对应的lambda,可以写作 {@code Student::setChildren} - * @param parentPredicate 树顶部的判断条件,可以写作 {@code s -> Objects.equals(s.getParentId(),0L) } - * @param isParallel 是否并行处理 - * @param 此处是元素类型 - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *
{@code
-	 * List studentTree = EasyStream.of(students).
-	 * 	.toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent);
-	 * }
- * @author VampireAchao - */ - public static , T> Collector> toTree( - final Function idGetter, - final Function pIdGetter, - final BiConsumer> childrenSetter, - final Predicate parentPredicate, - final boolean isParallel) { - final List parents = new ArrayList<>(); - return Collectors.collectingAndThen(filtering(e -> { - if (parentPredicate.test(e)) { - parents.add(e); - } - return idGetter.apply(e) != null; - }, groupingBy(pIdGetter)), - getChildrenFromMapByPidAndSet(idGetter, pIdValuesMap -> parents, childrenSetter, isParallel)); - } - - /** - * toTree的内联函数 - * 因为需要在当前传入数据里查找,所以这是一个结束操作 - * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} - * @param parentFactory 顶部数据工厂方法 - * @param childrenSetter children的setter对应的lambda,可以写作 {@code Student::setChildren} - * @param isParallel 是否并行处理 - * @param 此处是元素类型 - * @param 此处是id的泛型限制 - * @return list 组装好的树 - * @author VampireAchao - */ - private static , T> Function>, List> getChildrenFromMapByPidAndSet( - final Function idGetter, - final Function>, List> parentFactory, - final BiConsumer> childrenSetter, - final boolean isParallel) { - return pIdValuesMap -> { - EasyStream.of(pIdValuesMap.values(), isParallel).flat(Function.identity()) - .forEach(value -> { - final List children = pIdValuesMap.get(idGetter.apply(value)); - if (children != null) { - childrenSetter.accept(value, children); - } - }); - return parentFactory.apply(pIdValuesMap); - }; - } - - /** *

过滤

* diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java index 5c61433a0..258518624 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java @@ -205,83 +205,6 @@ public interface TerminableWrappedStream index.incrementAndGet(), valueMapper, (l, r) -> r); } - - /** - *

将集合转换为树,默认用 {@code parentId == null} 来判断树的根节点 - * 因为需要在当前传入数据里查找,所以这是一个结束操作
- * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param childrenSetter children的setter对应的lambda,可以写作{ @code Student::setChildren} - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *

{@code
-	 * List studentTree = EasyStream.of(students).
-	 * 	toTree(Student::getId, Student::getParentId, Student::setChildren);
-	 * }
- * @author VampireAchao - */ - default > List toTree( - final Function idGetter, - final Function pIdGetter, - final BiConsumer> childrenSetter) { - return collect(CollectorUtil.toTree(idGetter, pIdGetter, childrenSetter, isParallel())); - } - - /** - *

将集合转换为树,传入 {@code parentId == pidValue} 来判断树的根节点 - * 因为需要在当前传入数据里查找,所以这是一个结束操作
- * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param pIdValue parentId的值,支持 {@code null} - * @param childrenSetter children的setter对应的lambda,可以写作{ @code Student::setChildren} - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *

{@code
-	 * List studentTree = EasyStream.of(students).
-	 * 	toTree(Student::getId, Student::getParentId, 0L, Student::setChildren);
-	 * }
- * @author VampireAchao - */ - default > List toTree( - final Function idGetter, - final Function pIdGetter, - final R pIdValue, - final BiConsumer> childrenSetter) { - return collect(CollectorUtil.toTree(idGetter, pIdGetter, pIdValue, childrenSetter, isParallel())); - } - - /** - * 将集合转换为树,自定义根节点的判断条件 - * 因为需要在当前传入数据里查找,所以这是一个结束操作 - * - * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} - * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} - * @param childrenSetter children的setter对应的lambda,可以写作 {@code Student::setChildren} - * @param parentPredicate 树顶部的判断条件,可以写作 {@code s -> Objects.equals(s.getParentId(),0L) } - * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树
- * eg: - *
{@code
-	 * List studentTree = EasyStream.of(students).
-	 * 	.toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent);
-	 * }
- * @author VampireAchao - */ - default > List toTree( - final Function idGetter, - final Function pIdGetter, - final BiConsumer> childrenSetter, - final Predicate parentPredicate) { - return collect(CollectorUtil.toTree(idGetter, pIdGetter, childrenSetter, parentPredicate, isParallel())); - } - - - // endregion - // region ============ to zip ============ /** diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java index c5128e2f4..39a283f42 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java @@ -2,19 +2,14 @@ package cn.hutool.core.stream; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.BooleanUtil; -import lombok.Data; -import lombok.experimental.Tolerate; import org.junit.Assert; import org.junit.Test; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.Arrays.asList; import static java.util.Collections.singletonList; /** @@ -436,124 +431,4 @@ public class EasyStreamTest { Assert.assertTrue(EasyStream.of(1).isNotEmpty()); } - @Test - public void testToTree() { - Consumer test = o -> { - final List studentTree = EasyStream - .of( - Student.builder().id(1L).name("dromara").build(), - Student.builder().id(2L).name("baomidou").build(), - Student.builder().id(3L).name("hutool").parentId(1L).build(), - Student.builder().id(4L).name("sa-token").parentId(1L).build(), - Student.builder().id(5L).name("mybatis-plus").parentId(2L).build(), - Student.builder().id(6L).name("looly").parentId(3L).build(), - Student.builder().id(7L).name("click33").parentId(4L).build(), - Student.builder().id(8L).name("jobob").parentId(5L).build() - ) - // just 3 lambda,top parentId is null - .toTree(Student::getId, Student::getParentId, Student::setChildren); - Assert.assertEquals(asList( - Student.builder().id(1L).name("dromara") - .children(asList(Student.builder().id(3L).name("hutool").parentId(1L) - .children(singletonList(Student.builder().id(6L).name("looly").parentId(3L).build())) - .build(), - Student.builder().id(4L).name("sa-token").parentId(1L) - .children(singletonList(Student.builder().id(7L).name("click33").parentId(4L).build())) - .build())) - .build(), - Student.builder().id(2L).name("baomidou") - .children(singletonList( - Student.builder().id(5L).name("mybatis-plus").parentId(2L) - .children(singletonList( - Student.builder().id(8L).name("jobob").parentId(5L).build() - )) - .build())) - .build() - ), studentTree); - }; - test = test.andThen(o -> { - final List studentTree = EasyStream - .of( - Student.builder().id(1L).name("dromara").matchParent(true).build(), - Student.builder().id(2L).name("baomidou").matchParent(true).build(), - Student.builder().id(3L).name("hutool").parentId(1L).build(), - Student.builder().id(4L).name("sa-token").parentId(1L).build(), - Student.builder().id(5L).name("mybatis-plus").parentId(2L).build(), - Student.builder().id(6L).name("looly").parentId(3L).build(), - Student.builder().id(7L).name("click33").parentId(4L).build(), - Student.builder().id(8L).name("jobob").parentId(5L).build() - ) - // just 4 lambda ,top by condition - .toTree(Student::getId, Student::getParentId, Student::setChildren, s -> BooleanUtil.isTrue(s.getMatchParent())); - Assert.assertEquals(asList( - Student.builder().id(1L).name("dromara").matchParent(true) - .children(asList(Student.builder().id(3L).name("hutool").parentId(1L) - .children(singletonList(Student.builder().id(6L).name("looly").parentId(3L).build())) - .build(), - Student.builder().id(4L).name("sa-token").parentId(1L) - .children(singletonList(Student.builder().id(7L).name("click33").parentId(4L).build())) - .build())) - .build(), - Student.builder().id(2L).name("baomidou").matchParent(true) - .children(singletonList( - Student.builder().id(5L).name("mybatis-plus").parentId(2L) - .children(singletonList( - Student.builder().id(8L).name("jobob").parentId(5L).build() - )) - .build())) - .build() - ), studentTree); - }); - test.accept(new Object()); - } - - @Test - public void testFlatTree() { - final List studentTree = asList( - Student.builder().id(1L).name("dromara") - .children(asList(Student.builder().id(3L).name("hutool").parentId(1L) - .children(singletonList(Student.builder().id(6L).name("looly").parentId(3L).build())) - .build(), - Student.builder().id(4L).name("sa-token").parentId(1L) - .children(singletonList(Student.builder().id(7L).name("click33").parentId(4L).build())) - .build())) - .build(), - Student.builder().id(2L).name("baomidou") - .children(singletonList( - Student.builder().id(5L).name("mybatis-plus").parentId(2L) - .children(singletonList( - Student.builder().id(8L).name("jobob").parentId(5L).build() - )) - .build())) - .build() - ); - Assert.assertEquals(asList( - Student.builder().id(1L).name("dromara").build(), - Student.builder().id(2L).name("baomidou").build(), - Student.builder().id(3L).name("hutool").parentId(1L).build(), - Student.builder().id(4L).name("sa-token").parentId(1L).build(), - Student.builder().id(5L).name("mybatis-plus").parentId(2L).build(), - Student.builder().id(6L).name("looly").parentId(3L).build(), - Student.builder().id(7L).name("click33").parentId(4L).build(), - Student.builder().id(8L).name("jobob").parentId(5L).build() - ), EasyStream.of(studentTree).flatTree(Student::getChildren, Student::setChildren).sorted(Comparator.comparingLong(Student::getId)).toList()); - - } - - @Data - @lombok.Builder - public static class Student { - private String name; - private Integer age; - private Long id; - private Long parentId; - private List children; - private Boolean matchParent; - - @Tolerate - public Student() { - // this is an accessible parameterless constructor. - } - } - } From 80287ec19f37953f3d3f2f406937b121324fa0b4 Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 26 Nov 2022 22:43:38 +0800 Subject: [PATCH 02/18] =?UTF-8?q?:trollface:=20=E6=96=B0=E5=A2=9EBeanTree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/tree/BeanTree.java | 207 ++++++++++++++++++ .../cn/hutool/core/tree/BeanTreeTest.java | 129 +++++++++++ 2 files changed, 336 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/tree/BeanTreeTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java new file mode 100644 index 000000000..bf5ac7aa1 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -0,0 +1,207 @@ +package cn.hutool.core.tree; + +import cn.hutool.core.lang.Opt; +import cn.hutool.core.lang.func.SerBiConsumer; +import cn.hutool.core.lang.func.SerConsumer; +import cn.hutool.core.lang.func.SerFunction; +import cn.hutool.core.lang.func.SerPredicate; +import cn.hutool.core.stream.EasyStream; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * 本类是用于构建树的工具类,特点是采取lambda,以及满足指定类型的Bean进行树操作 + * Bean需要满足三个属性: + *
    + *
  • 包含不为null的主键(例如id)
  • + *
  • 包含容许为null的关联外键(例如parentId)
  • + *
  • 包含自身的子集,例如类型为List的children
  • + *
+ * 本类的构建方法是通过{@code BeanTree.of} 进行构建,例如: + *
{@code final BeanTree beanTree = BeanTree.of(JavaBean::getId, JavaBean::getParentId, null, JavaBean::getChildren, JavaBean::setChildren);}
+ * 得到的BeanTree实例可以调用toTree方法,将集合转换为树,例如: + *
{@code final List javaBeanTree = beanTree.toTree(originJavaBeanList);}
+ * 也可以将已有的树转换为集合,例如: + *
{@code final List javaBeanList = beanTree.flat(originJavaBeanTree);}
+ * + * @author VampireAchao + * 最后,引用一句电影经典台词: 无处安放的双手,以及无处安放的灵魂。——《Hello!树先生》 + */ +public class BeanTree> { + + /** + * 主键getter + */ + private final SerFunction idGetter; + /** + * 外键getter + */ + private final SerFunction pidGetter; + /** + * 外键匹配值(保留此属性主要是性能较外键条件匹配稍微好一点) + */ + private final R pidValue; + /** + * 外键匹配条件 + */ + private final SerPredicate parentPredicate; + /** + * 子集getter + */ + private final SerFunction> childrenGetter; + /** + * 子集setter + */ + private final SerBiConsumer> childrenSetter; + + private BeanTree(final SerFunction idGetter, + final SerFunction pidGetter, + final R pidValue, + final SerPredicate parentPredicate, + final SerFunction> childrenGetter, + final SerBiConsumer> childrenSetter) { + this.idGetter = idGetter; + this.pidGetter = pidGetter; + this.pidValue = pidValue; + this.parentPredicate = parentPredicate; + this.childrenGetter = childrenGetter; + this.childrenSetter = childrenSetter; + } + + /** + * 构建BeanTree + * + * @param idGetter 主键getter,例如 {@code JavaBean::getId} + * @param pidGetter 外键getter,例如 {@code JavaBean::getParentId} + * @param pidValue 外键的值,例如 {@code null} + * @param childrenGetter 子集getter,例如 {@code JavaBean::getChildren} + * @param childrenSetter 子集setter,例如 {@code JavaBean::setChildren} + * @param Bean类型 + * @param 主键、外键类型 + * @return BeanTree + */ + public static > BeanTree of(final SerFunction idGetter, + final SerFunction pidGetter, + final R pidValue, + final SerFunction> childrenGetter, + final SerBiConsumer> childrenSetter) { + return new BeanTree<>(idGetter, pidGetter, pidValue, null, childrenGetter, childrenSetter); + } + + /** + * 构建BeanTree + * + * @param idGetter 主键getter,例如 {@code JavaBean::getId} + * @param pidGetter 外键getter,例如 {@code JavaBean::getParentId} + * @param parentPredicate 父节点判断条件,例如 {@code o -> Objects.isNull(o.getParentId())} + * @param childrenGetter 子集getter,例如 {@code JavaBean::getChildren} + * @param childrenSetter 子集setter,例如 {@code JavaBean::setChildren} + * @param Bean类型 + * @param 主键、外键类型 + * @return BeanTree + */ + public static > BeanTree ofMatch(final SerFunction idGetter, + final SerFunction pidGetter, + final SerPredicate parentPredicate, + final SerFunction> childrenGetter, + final SerBiConsumer> childrenSetter) { + return new BeanTree<>(idGetter, pidGetter, null, parentPredicate, childrenGetter, childrenSetter); + } + + /** + * 将集合转换为树 + * + * @param list 集合 + * @return 转换后的树 + */ + public List toTree(final List list) { + if (Objects.isNull(parentPredicate)) { + final Map> pIdValuesMap = EasyStream.of(list).filter(e -> Objects.nonNull(idGetter.apply(e))).group(pidGetter); + final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); + getChildrenFromMapByPidAndSet(pIdValuesMap); + return parents; + } + final List parents = new ArrayList<>(list.size()); + final Map> pIdValuesMap = EasyStream.of(list).filter(e -> { + if (parentPredicate.test(e)) { + parents.add(e); + } + return Objects.nonNull(idGetter.apply(e)); + }).group(pidGetter); + getChildrenFromMapByPidAndSet(pIdValuesMap); + return parents; + } + + /** + * 将树扁平化为集合,相当于将树里的所有节点都放到一个集合里 + * + * @param tree 树 + * @return 集合 + */ + @SuppressWarnings("unchecked") + public List flat(final List tree) { + final AtomicReference>> recursiveRef = new AtomicReference<>(); + final Function> recursive = e -> EasyStream.of(childrenGetter.apply(e)).flat(recursiveRef.get()).unshift(e); + recursiveRef.set(recursive); + return EasyStream.of(tree).flat(recursive).peek(e -> childrenSetter.accept(e, null)).toList(); + } + + /** + * 树的过滤操作,本方法一般适用于寻找某人所在部门以及所有上级部门类似的逻辑 + * 通过{@link SerPredicate}指定的过滤规则,本节点或子节点满足过滤条件,则保留当前节点,否则抛弃节点及其子节点 + * + * @param tree 树 + * @param condition 节点过滤规则函数,只需处理本级节点本身即可,{@link SerPredicate#test(Object)}为{@code true}保留 + * @return 过滤后的树 + */ + public List filter(final List tree, final SerPredicate condition) { + final AtomicReference> recursiveRef = new AtomicReference<>(); + final Predicate recursive = SerPredicate.multiOr(condition::test, + e -> Opt.ofEmptyAble(childrenGetter.apply(e)) + .map(children -> EasyStream.of(children).filter(recursiveRef.get()).toList()) + .peek(children -> childrenSetter.accept(e, children)) + .filter(s -> !s.isEmpty()).isPresent()); + recursiveRef.set(recursive); + return EasyStream.of(tree).filter(recursive).toList(); + } + + /** + * 树节点遍历操作 + * + * @param tree 数 + * @param action 操作 + * @return 树 + */ + public List forEach(final List tree, final SerConsumer action) { + final AtomicReference> recursiveRef = new AtomicReference<>(); + final Consumer recursive = SerConsumer.multi(action::accept, + e -> Opt.ofEmptyAble(childrenGetter.apply(e)) + .peek(children -> EasyStream.of(children).forEach(recursiveRef.get()))); + recursiveRef.set(recursive); + EasyStream.of(tree).forEach(recursive); + return tree; + } + + /** + * 内联函数,获取子集并设置到父节点 + * + * @param pIdValuesMap 父id与子集的映射 + */ + private void getChildrenFromMapByPidAndSet(final Map> pIdValuesMap) { + EasyStream.of(pIdValuesMap.values()).flat(Function.identity()) + .forEach(value -> { + final List children = pIdValuesMap.get(idGetter.apply(value)); + if (children != null) { + childrenSetter.accept(value, children); + } + }); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/tree/BeanTreeTest.java b/hutool-core/src/test/java/cn/hutool/core/tree/BeanTreeTest.java new file mode 100644 index 000000000..5db467bd9 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/tree/BeanTreeTest.java @@ -0,0 +1,129 @@ +package cn.hutool.core.tree; + +import cn.hutool.core.stream.EasyStream; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Comparator; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; + +/** + * TreeHelperTest + * + * @author VampireAchao + */ +public class BeanTreeTest { + + @Data + @Builder + private static class JavaBean { + @Tolerate + public JavaBean() { + // this is an accessible parameterless constructor. + } + + private String name; + private Integer age; + private Long id; + private Long parentId; + private List children; + private Boolean matchParent; + } + + List originJavaBeanList; + List originJavaBeanTree; + BeanTree beanTree; + + @Before + public void setUp() { + originJavaBeanList = EasyStream + .of( + JavaBean.builder().id(1L).name("dromara").matchParent(true).build(), + JavaBean.builder().id(2L).name("baomidou").matchParent(true).build(), + JavaBean.builder().id(3L).name("hutool").parentId(1L).build(), + JavaBean.builder().id(4L).name("sa-token").parentId(1L).build(), + JavaBean.builder().id(5L).name("mybatis-plus").parentId(2L).build(), + JavaBean.builder().id(6L).name("looly").parentId(3L).build(), + JavaBean.builder().id(7L).name("click33").parentId(4L).build(), + JavaBean.builder().id(8L).name("jobob").parentId(5L).build() + ).toList(); + originJavaBeanTree = asList( + JavaBean.builder().id(1L).name("dromara").matchParent(true) + .children(asList( + JavaBean.builder().id(3L).name("hutool").parentId(1L) + .children(singletonList(JavaBean.builder().id(6L).name("looly").parentId(3L).build())) + .build(), + JavaBean.builder().id(4L).name("sa-token").parentId(1L) + .children(singletonList(JavaBean.builder().id(7L).name("click33").parentId(4L).build())) + .build())) + .build(), + JavaBean.builder().id(2L).name("baomidou").matchParent(true) + .children(singletonList( + JavaBean.builder().id(5L).name("mybatis-plus").parentId(2L) + .children(singletonList( + JavaBean.builder().id(8L).name("jobob").parentId(5L).build() + )) + .build())) + .build() + ); + beanTree = BeanTree.of(JavaBean::getId, JavaBean::getParentId, null, JavaBean::getChildren, JavaBean::setChildren); + } + + @Test + public void testToTree() { + final List javaBeanTree = beanTree.toTree(originJavaBeanList); + Assert.assertEquals(originJavaBeanTree, javaBeanTree); + final BeanTree conditionBeanTree = BeanTree.ofMatch(JavaBean::getId, JavaBean::getParentId, s -> Boolean.TRUE.equals(s.getMatchParent()), JavaBean::getChildren, JavaBean::setChildren); + Assert.assertEquals(originJavaBeanTree, conditionBeanTree.toTree(originJavaBeanList)); + } + + @Test + public void testFlat() { + final List javaBeanList = beanTree.flat(originJavaBeanTree); + javaBeanList.sort(Comparator.comparing(JavaBean::getId)); + Assert.assertEquals(originJavaBeanList, javaBeanList); + } + + @Test + public void testFilter() { + final List javaBeanTree = beanTree.filter(originJavaBeanTree, s -> "looly".equals(s.getName())); + Assert.assertEquals(singletonList( + JavaBean.builder().id(1L).name("dromara").matchParent(true) + .children(singletonList(JavaBean.builder().id(3L).name("hutool").parentId(1L) + .children(singletonList(JavaBean.builder().id(6L).name("looly").parentId(3L).build())) + .build())) + .build()), + javaBeanTree); + } + + @Test + public void testForeach() { + final List javaBeanList = beanTree.forEach(originJavaBeanTree, s -> s.setName("【open source】" + s.getName())); + Assert.assertEquals(asList( + JavaBean.builder().id(1L).name("【open source】dromara").matchParent(true) + .children(asList(JavaBean.builder().id(3L).name("【open source】hutool").parentId(1L) + .children(singletonList(JavaBean.builder().id(6L).name("【open source】looly").parentId(3L).build())) + .build(), + JavaBean.builder().id(4L).name("【open source】sa-token").parentId(1L) + .children(singletonList(JavaBean.builder().id(7L).name("【open source】click33").parentId(4L).build())) + .build())) + .build(), + JavaBean.builder().id(2L).name("【open source】baomidou").matchParent(true) + .children(singletonList( + JavaBean.builder().id(5L).name("【open source】mybatis-plus").parentId(2L) + .children(singletonList( + JavaBean.builder().id(8L).name("【open source】jobob").parentId(5L).build() + )) + .build())) + .build() + ), javaBeanList); + } + +} From 1af3b0a85333f46b31360ef5eb571b95bd120b91 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Sun, 27 Nov 2022 11:44:15 +0000 Subject: [PATCH 03/18] Apply all suggestions from code review --- .../src/main/java/cn/hutool/core/tree/BeanTree.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index bf5ac7aa1..0c2661534 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -80,7 +80,7 @@ public class BeanTree> { * * @param idGetter 主键getter,例如 {@code JavaBean::getId} * @param pidGetter 外键getter,例如 {@code JavaBean::getParentId} - * @param pidValue 外键的值,例如 {@code null} + * @param pidValue 根节点的外键值,例如 {@code null} * @param childrenGetter 子集getter,例如 {@code JavaBean::getChildren} * @param childrenSetter 子集setter,例如 {@code JavaBean::setChildren} * @param Bean类型 @@ -100,7 +100,7 @@ public class BeanTree> { * * @param idGetter 主键getter,例如 {@code JavaBean::getId} * @param pidGetter 外键getter,例如 {@code JavaBean::getParentId} - * @param parentPredicate 父节点判断条件,例如 {@code o -> Objects.isNull(o.getParentId())} + * @param parentPredicate 根节点判断条件,例如 {@code o -> Objects.isNull(o.getParentId())} * @param childrenGetter 子集getter,例如 {@code JavaBean::getChildren} * @param childrenSetter 子集setter,例如 {@code JavaBean::setChildren} * @param Bean类型 @@ -155,7 +155,8 @@ public class BeanTree> { /** * 树的过滤操作,本方法一般适用于寻找某人所在部门以及所有上级部门类似的逻辑 - * 通过{@link SerPredicate}指定的过滤规则,本节点或子节点满足过滤条件,则保留当前节点,否则抛弃节点及其子节点 + * 通过{@link SerPredicate}指定的过滤规则,本节点或子节点满足过滤条件,则保留当前节点,否则抛弃节点及其子节点
+ * 即,一条路径上只要有一个节点符合条件,就保留整条路径上的节点 * * @param tree 树 * @param condition 节点过滤规则函数,只需处理本级节点本身即可,{@link SerPredicate#test(Object)}为{@code true}保留 From 24c066db43c1391b8fd0b1510a867903fb3d167a Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Sun, 27 Nov 2022 20:27:49 +0800 Subject: [PATCH 04/18] :trollface: Apply all suggestions from code review --- .../java/cn/hutool/core/tree/BeanTree.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 0c2661534..60eaf9aa0 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -1,11 +1,10 @@ package cn.hutool.core.tree; +import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Opt; -import cn.hutool.core.lang.func.SerBiConsumer; -import cn.hutool.core.lang.func.SerConsumer; -import cn.hutool.core.lang.func.SerFunction; -import cn.hutool.core.lang.func.SerPredicate; +import cn.hutool.core.lang.func.*; import cn.hutool.core.stream.EasyStream; +import cn.hutool.core.text.StrUtil; import java.util.ArrayList; import java.util.List; @@ -67,12 +66,15 @@ public class BeanTree> { final SerPredicate parentPredicate, final SerFunction> childrenGetter, final SerBiConsumer> childrenSetter) { - this.idGetter = idGetter; + this.idGetter = Objects.requireNonNull(idGetter, "idGetter must not be null"); this.pidGetter = pidGetter; this.pidValue = pidValue; this.parentPredicate = parentPredicate; - this.childrenGetter = childrenGetter; - this.childrenSetter = childrenSetter; + if (Objects.isNull(pidGetter) && Objects.isNull(parentPredicate)) { + throw new UtilException("pidGetter and parentPredicate can not be null at the same time"); + } + this.childrenGetter = Objects.requireNonNull(childrenGetter, "childrenGetter must not be null"); + this.childrenSetter = Objects.requireNonNull(childrenSetter, "childrenSetter must not be null"); } /** @@ -123,19 +125,22 @@ public class BeanTree> { */ public List toTree(final List list) { if (Objects.isNull(parentPredicate)) { - final Map> pIdValuesMap = EasyStream.of(list).filter(e -> Objects.nonNull(idGetter.apply(e))).group(pidGetter); + final Map> pIdValuesMap = EasyStream.of(list) + .peek(e -> Objects.requireNonNull(idGetter.apply(e), + () -> StrUtil.format("primary key {} must not null", LambdaUtil.getFieldName(idGetter)) + )).group(pidGetter); final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); - getChildrenFromMapByPidAndSet(pIdValuesMap); + findChildren(list, pIdValuesMap); return parents; } final List parents = new ArrayList<>(list.size()); - final Map> pIdValuesMap = EasyStream.of(list).filter(e -> { + final Map> pIdValuesMap = EasyStream.of(list).peek(e -> { if (parentPredicate.test(e)) { parents.add(e); } - return Objects.nonNull(idGetter.apply(e)); + Objects.requireNonNull(idGetter.apply(e)); }).group(pidGetter); - getChildrenFromMapByPidAndSet(pIdValuesMap); + findChildren(list, pIdValuesMap); return parents; } @@ -163,6 +168,7 @@ public class BeanTree> { * @return 过滤后的树 */ public List filter(final List tree, final SerPredicate condition) { + Objects.requireNonNull(condition, "filter condition must be not null"); final AtomicReference> recursiveRef = new AtomicReference<>(); final Predicate recursive = SerPredicate.multiOr(condition::test, e -> Opt.ofEmptyAble(childrenGetter.apply(e)) @@ -181,6 +187,7 @@ public class BeanTree> { * @return 树 */ public List forEach(final List tree, final SerConsumer action) { + Objects.requireNonNull(action, "action must be not null"); final AtomicReference> recursiveRef = new AtomicReference<>(); final Consumer recursive = SerConsumer.multi(action::accept, e -> Opt.ofEmptyAble(childrenGetter.apply(e)) @@ -193,16 +200,16 @@ public class BeanTree> { /** * 内联函数,获取子集并设置到父节点 * + * @param list 集合 * @param pIdValuesMap 父id与子集的映射 */ - private void getChildrenFromMapByPidAndSet(final Map> pIdValuesMap) { - EasyStream.of(pIdValuesMap.values()).flat(Function.identity()) - .forEach(value -> { - final List children = pIdValuesMap.get(idGetter.apply(value)); - if (children != null) { - childrenSetter.accept(value, children); - } - }); + private void findChildren(final List list, final Map> pIdValuesMap) { + EasyStream.of(list).forEach(value -> { + final List children = pIdValuesMap.get(idGetter.apply(value)); + if (children != null) { + childrenSetter.accept(value, children); + } + }); } } From a631f7e9159607685cb95babe34b9f45597ad90e Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Sun, 27 Nov 2022 13:09:22 +0000 Subject: [PATCH 05/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 60eaf9aa0..1e1346a75 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -146,6 +146,7 @@ public class BeanTree> { /** * 将树扁平化为集合,相当于将树里的所有节点都放到一个集合里 + *

本方法会主动将节点的子集合字段置为null

* * @param tree 树 * @return 集合 From d5879a0abd80caf6efffe9dd286d3f4ece2da476 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Sun, 27 Nov 2022 14:31:00 +0000 Subject: [PATCH 06/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 1e1346a75..64905eb4c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -126,8 +126,7 @@ public class BeanTree> { public List toTree(final List list) { if (Objects.isNull(parentPredicate)) { final Map> pIdValuesMap = EasyStream.of(list) - .peek(e -> Objects.requireNonNull(idGetter.apply(e), - () -> StrUtil.format("primary key {} must not null", LambdaUtil.getFieldName(idGetter)) + .peek(e -> Objects.requireNonNull(idGetter.apply(e), "The id of tree node must not be null") )).group(pidGetter); final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); findChildren(list, pIdValuesMap); From 0660c75057bc461478a3fc7479d9346ff4ac21e3 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Sun, 27 Nov 2022 14:31:11 +0000 Subject: [PATCH 07/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 64905eb4c..df703bb4e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -132,7 +132,7 @@ public class BeanTree> { findChildren(list, pIdValuesMap); return parents; } - final List parents = new ArrayList<>(list.size()); + final List parents = new ArrayList<>(); final Map> pIdValuesMap = EasyStream.of(list).peek(e -> { if (parentPredicate.test(e)) { parents.add(e); From bd9439922e5b00ad497131fb6683a85c91d3723f Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Sun, 27 Nov 2022 22:36:17 +0800 Subject: [PATCH 08/18] :trollface: Apply all suggestions from code review --- .../src/main/java/cn/hutool/core/tree/BeanTree.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 60eaf9aa0..75743b069 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -1,6 +1,5 @@ package cn.hutool.core.tree; -import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.func.*; import cn.hutool.core.stream.EasyStream; @@ -67,12 +66,9 @@ public class BeanTree> { final SerFunction> childrenGetter, final SerBiConsumer> childrenSetter) { this.idGetter = Objects.requireNonNull(idGetter, "idGetter must not be null"); - this.pidGetter = pidGetter; + this.pidGetter = Objects.requireNonNull(pidGetter, "pidGetter must not be null"); this.pidValue = pidValue; this.parentPredicate = parentPredicate; - if (Objects.isNull(pidGetter) && Objects.isNull(parentPredicate)) { - throw new UtilException("pidGetter and parentPredicate can not be null at the same time"); - } this.childrenGetter = Objects.requireNonNull(childrenGetter, "childrenGetter must not be null"); this.childrenSetter = Objects.requireNonNull(childrenSetter, "childrenSetter must not be null"); } @@ -114,7 +110,7 @@ public class BeanTree> { final SerPredicate parentPredicate, final SerFunction> childrenGetter, final SerBiConsumer> childrenSetter) { - return new BeanTree<>(idGetter, pidGetter, null, parentPredicate, childrenGetter, childrenSetter); + return new BeanTree<>(idGetter, pidGetter, null, Objects.requireNonNull(parentPredicate, "parentPredicate must not be null"), childrenGetter, childrenSetter); } /** From ce576a1d333caf2ca594a20f75779e0653598c6c Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 04:01:34 +0000 Subject: [PATCH 09/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index d120ad34a..e4e78a638 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -194,7 +194,7 @@ public class BeanTree> { } /** - * 内联函数,获取子集并设置到父节点 + * 内联函数,设置每个节点的子集 * * @param list 集合 * @param pIdValuesMap 父id与子集的映射 From f1f741bb58e8b4279658fc35ef6d07b5d0aaa6b4 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 08:54:26 +0000 Subject: [PATCH 10/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- .../src/main/java/cn/hutool/core/tree/BeanTree.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index e4e78a638..7c95b275b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -120,10 +120,13 @@ public class BeanTree> { * @return 转换后的树 */ public List toTree(final List list) { - if (Objects.isNull(parentPredicate)) { - final Map> pIdValuesMap = EasyStream.of(list) - .peek(e -> Objects.requireNonNull(idGetter.apply(e), "The id of tree node must not be null") - )).group(pidGetter); + if (CollUtil.isEmpty(list)) { + return ListUtil.zero(); + } + if (Objects.isNull(parentPredicate)) { + final Map> pIdValuesMap = EasyStream.of(list) + .peek(e -> Objects.requireNonNull(idGetter.apply(e), "The id of tree node must not be null") + ).group(pidGetter); final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); findChildren(list, pIdValuesMap); return parents; From 08526cdea93c1d2284b4b250d041d00590aa1b4e Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 08:54:37 +0000 Subject: [PATCH 11/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 7c95b275b..60dd11870 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -3,7 +3,6 @@ package cn.hutool.core.tree; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.func.*; import cn.hutool.core.stream.EasyStream; -import cn.hutool.core.text.StrUtil; import java.util.ArrayList; import java.util.List; From 5a0478b149c785d4a9be6dbb3ee341ab3359432b Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 08:54:50 +0000 Subject: [PATCH 12/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 60dd11870..257b8a947 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -1,5 +1,7 @@ package cn.hutool.core.tree; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.func.*; import cn.hutool.core.stream.EasyStream; From 55188ca965b8ca83516b6d6a404b6aa4b975c693 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 08:55:03 +0000 Subject: [PATCH 13/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- .../src/main/java/cn/hutool/core/tree/BeanTree.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 257b8a947..396d2c304 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -204,12 +204,12 @@ public class BeanTree> { * @param pIdValuesMap 父id与子集的映射 */ private void findChildren(final List list, final Map> pIdValuesMap) { - EasyStream.of(list).forEach(value -> { - final List children = pIdValuesMap.get(idGetter.apply(value)); + for (T node : list) { + final List children = pIdValuesMap.get(idGetter.apply(node)); if (children != null) { - childrenSetter.accept(value, children); + childrenSetter.accept(node, children); } - }); + } } } From 38e5a318703435f2aa4ba0f4e59f65b63b70ae91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=B6=85?= Date: Mon, 28 Nov 2022 09:06:53 +0000 Subject: [PATCH 14/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 396d2c304..7c3befe35 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -31,6 +31,8 @@ import java.util.function.Predicate; *
{@code final List javaBeanList = beanTree.flat(originJavaBeanTree);}
* * @author VampireAchao + * @author emptypoint + * @author CreateSequence * 最后,引用一句电影经典台词: 无处安放的双手,以及无处安放的灵魂。——《Hello!树先生》 */ public class BeanTree> { From 720476f95c1f21affca8c5604ce39ff8d417f38c Mon Sep 17 00:00:00 2001 From: Createsequence <841396397@qq.com> Date: Mon, 28 Nov 2022 09:34:05 +0000 Subject: [PATCH 15/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 7c3befe35..247776753 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -1,6 +1,7 @@ package cn.hutool.core.tree; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.func.*; From 01bc7e6c16e0af5f5a1e38a4734dc95ec2a65fa9 Mon Sep 17 00:00:00 2001 From: Createsequence <841396397@qq.com> Date: Mon, 28 Nov 2022 09:34:49 +0000 Subject: [PATCH 16/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 247776753..b545895e6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -129,7 +129,7 @@ public class BeanTree> { } if (Objects.isNull(parentPredicate)) { final Map> pIdValuesMap = EasyStream.of(list) - .peek(e -> Objects.requireNonNull(idGetter.apply(e), "The id of tree node must not be null") + .peek(e -> Assert.notNull(idGetter.apply(e), "The id of tree node must not be null {}", e)) ).group(pidGetter); final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); findChildren(list, pIdValuesMap); From 39cf53400610a8a7d350ec622172ca583877eb50 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 09:48:23 +0000 Subject: [PATCH 17/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java :trollface: --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index b545895e6..184b99d43 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -129,8 +129,8 @@ public class BeanTree> { } if (Objects.isNull(parentPredicate)) { final Map> pIdValuesMap = EasyStream.of(list) - .peek(e -> Assert.notNull(idGetter.apply(e), "The id of tree node must not be null {}", e)) - ).group(pidGetter); + .peek(e -> Objects.requireNonNull(idGetter.apply(e), () -> "The id of tree node must not be null " + e)) + .group(pidGetter); final List parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); findChildren(list, pIdValuesMap); return parents; From 2c47b834ad304eec2fec1c748272679cbf18bd66 Mon Sep 17 00:00:00 2001 From: emptypoint <1215582715@qq.com> Date: Mon, 28 Nov 2022 09:57:56 +0000 Subject: [PATCH 18/18] Update hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java --- hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java index 184b99d43..aa0cf12df 100644 --- a/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java +++ b/hutool-core/src/main/java/cn/hutool/core/tree/BeanTree.java @@ -1,7 +1,6 @@ package cn.hutool.core.tree; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.func.*;