基于hutool树构建工具(TreeUtil)封装的搜索树工具类

单调树结构搜索实现

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.tree.Tree;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 支持多参数检索,保留树层级结构,即匹配到子节点时父节点也保留
 * 
 * 下面是伪代码
 * <pre>{@code
 * List<Tree<Long>> source = new ArrayList<>();
 * List<Tree<Long>> result = TreeSearcher.of(source)
 * .applyEq("key1", "value1")
 * .applyEq("key2", 2)
 * .applyLike("key3", "value3")
 * .getResult();
 * }
 * </pre>
 *
 * @author Alone
 */
public class TreeSearcher<T, R> {

    private final List<Tree<T>> source;
    private final Map<String, SearchItem> searchMap;

    private TreeSearcher(List<Tree<T>> source) {
        this.source = source;
        this.searchMap = new HashMap<>(4);
    }

    @SuppressWarnings("unused")
    public static <T, R> TreeSearcher<T, R> of(List<Tree<T>> source, Class<R> targetClass) {
        return new TreeSearcher<>(source);
    }
    public static <T, R> TreeSearcher<T, R> of(List<Tree<T>> source) {
        return new TreeSearcher<>(source);
    }

    public TreeSearcher<T, R> applyEq(boolean condition, Func1<R, ?> key, Number data) {
        return apply(condition, LambdaUtil.getFieldName(key), data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyEq(Func1<R, ?> key, Number data) {
        return apply(true, LambdaUtil.getFieldName(key), data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyEq(Func1<R, ?> key, Object data) {
        return apply(true, LambdaUtil.getFieldName(key), data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyLike(Func1<R, ?> key, Object data) {
        return apply(true, LambdaUtil.getFieldName(key), data, SearchModel.LIKE);
    }

    public TreeSearcher<T, R> applyEq(boolean condition, Func1<R, ?> key, Object data) {
        return apply(condition, LambdaUtil.getFieldName(key), data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyLike(boolean condition, Func1<R, ?> key, Object data) {
        return apply(condition, LambdaUtil.getFieldName(key), data, SearchModel.LIKE);
    }

    public TreeSearcher<T, R> applyEq(boolean condition, String key, Number data) {
        return apply(condition, key, data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyEq(String key, Number data) {
        return apply(true, key, data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyEq(String key, Object data) {
        return apply(true, key, data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyLike(String key, Object data) {
        return apply(true, key, data, SearchModel.LIKE);
    }

    public TreeSearcher<T, R> applyEq(boolean condition, String key, Object data) {
        return apply(condition, key, data, SearchModel.EQ);
    }

    public TreeSearcher<T, R> applyLike(boolean condition, String key, Object data) {
        return apply(condition, key, data, SearchModel.LIKE);
    }

    private TreeSearcher<T, R> apply(boolean condition, String key, Object data, SearchModel model) {
        if (condition) {
            BizAssert.isNotBlank(key, "key不能空");
            SearchItem searchItem = new SearchItem(data, model);
            searchMap.put(key, searchItem);
        }
        return this;
    }

    public List<Tree<T>> getResult() {
        if (CollUtil.isEmpty(this.source)) {
            return Collections.emptyList();
        }
        if (CollUtil.isEmpty(searchMap)) {
            return this.source;
        }
        return search();
    }

    /**
     * hutool自带的搜索逻辑
     */
    public List<Tree<T>> getResult2() {
        if (CollUtil.isEmpty(this.source)) {
            return Collections.emptyList();
        }
        if (CollUtil.isEmpty(searchMap)) {
            return this.source;
        }
        return search2();
    }

    private List<Tree<T>> search2() {
        return this.source.stream()
                .map(tree -> tree.filter(match))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<Tree<T>> search() {
        return this.source.stream()
                .map(item -> doSearch(item, false))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private Tree<T> doSearch(Tree<T> tree, boolean pMatches) {
        if (pMatches) {
            return tree;
        }
        boolean currentMatches = match(tree);
        if (tree.hasChild()) {
            List<Tree<T>> children = tree.getChildren().stream()
                    .map(item -> doSearch(item, currentMatches))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            tree.setChildren(children);
            if (CollUtil.isNotEmpty(children)) {
                return tree;
            }
        }
        if (currentMatches) {
            return tree;
        } else {
            return null;
        }
    }

    private final Filter<Tree<T>> match = this::match;
    private boolean match(Tree<T> tree) {
        return searchMap.keySet().stream().allMatch(key -> {
            SearchItem searchItem = searchMap.get(key);
            Object val = tree.get(key);
            if (Objects.isNull(val) && Objects.isNull(searchItem.getData())) {
                return false;
            }
            boolean res = false;
            if (val instanceof Number) {
                BizAssert.isTrue(SearchModel.EQ.equals(searchItem.getModel()), "数字类型值只支持等值比较");
                BizAssert.isTrue(searchItem.getData() instanceof Number, "数字类型值只支持匹配数字");
                res = val == searchItem.getData();
            } else {
                String valStr = val.toString();
                if (SearchModel.EQ.equals(searchItem.getModel())) {
                    res = valStr.trim().equals(searchItem.getData());
                }
                if (SearchModel.LIKE.equals(searchItem.getModel())) {
                    String regex = ".*" + searchItem.getData() + ".*";
                    res = valStr.matches(regex);
                }
            }
            return res;
        });
    }

    @SuppressWarnings("all")
    private enum SearchModel {
        EQ,
        LIKE
    }

    @Getter
    @RequiredArgsConstructor
    private static class SearchItem {
        private final Object data;
        private final SearchModel model;
    }

}

异构树结构搜索实现

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.tree.Tree;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * 异构多子树搜索实现,搜索逻辑与TreeSearcher一致
 *
 * @author Alone
 * @see TreeSearcher
 */
@SuppressWarnings({"rawtypes", "unchecked"})
abstract class MultiTreeSearcherBase<T, Children extends MultiTreeSearcherBase<T, Children>> {

    private final List<Tree<T>> source;

    private final Map<
            Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>>,
            Predicate<Tree>
            > matcherMap;

    /**
     * 主树的孩子节点的 getter setter
     */
    protected final Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> defaultKey;

    @SuppressWarnings("unchecked")
    protected final Children typedThis = (Children) this;

    protected MultiTreeSearcherBase(List<Tree<T>> source) {
        this.source = source;
        this.matcherMap = new HashMap<>(1);
        this.defaultKey = Pair.of(Tree::getChildren, Tree::setChildren);
    }

    protected Children eq(Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> key, Function<Tree, ?> valueFunc, Object value) {
        and(key, tree -> valueFunc.apply(tree).equals(value));
        return typedThis;
    }

    protected Children like(Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> key, Function<Tree, String> valueFunc, String value) {
        String regex = ".*" + value + ".*";
        and(key, tree -> valueFunc.apply(tree).matches(regex));
        return typedThis;
    }

    private void and(Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> key, Predicate<Tree>... predicate) {
        Predicate<Tree> matcher = matcherMap.get(key);
        if (Objects.isNull(matcher)) {
            matcher = Predicates.and(predicate);
        } else {
            matcher = Predicates.and(matcher, Predicates.and(predicate));
        }
        matcherMap.put(key, matcher);
    }

    public List<Tree<T>> getResult() {
        if (CollUtil.isEmpty(this.source)) {
            return Collections.emptyList();
        }
        if (CollUtil.isEmpty(matcherMap.values())) {
            return this.source;
        }
        return search();
    }

    private List<Tree<T>> search() {
        return this.source.stream()
                .map(item -> {
                    Tree<T> res = null;
                    for (Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> pair : matcherMap.keySet()) {
                        Tree tree = searchTree(item, pair, matcherMap.get(pair), false);
                        if (Objects.nonNull(tree)) {
                            res = tree;
                        }
                    }
                    return res;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private Tree searchTree(Tree tree, Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> pair, Predicate<Tree> matcher, boolean pMatches) {
        if (pMatches) {
            return tree;
        }
        boolean currentMatches = matcher.test(tree);
        List<Tree> child = pair.getKey().apply(tree);
        if (CollUtil.isNotEmpty(child)) {
            child = child.stream()
                    .map(item -> searchTree(item, defaultKey, matcher, currentMatches))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            pair.getValue().accept(tree, child);
            if (CollUtil.isNotEmpty(child)) {
                return tree;
            }
        }
        if (currentMatches) {
            return tree;
        } else {
            return null;
        }
    }
}
import cn.hutool.core.lang.Pair;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.lang.tree.Tree;
import com.google.common.base.Function;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;

/**
 * 基于MultiTreeSearcherBase封装的便捷api
 *
 * @author Alone
 * @see MultiTreeSearcherBase
 */
@SuppressWarnings("all")
public class MultiTreeSearcher<T, R> extends MultiTreeSearcherBase<T, MultiTreeSearcher<T, R>> {

    public static <T, R> MultiTreeSearcher<T, R> of(List<Tree<T>> source) {
        return new MultiTreeSearcher<>(source);
    }

    public static <T, R> MultiTreeSearcher<T, R> of(List<Tree<T>> source, Class<R> clazz) {
        return new MultiTreeSearcher<>(source);
    }

    private final Function<Func1<R, ?>, Function<Tree, String>> strValueFunc = key -> {
        String fieldName = LambdaUtil.getFieldName(key);
        return tree -> tree.get(fieldName).toString();
    };

    private final Function<Func1<R, ?>, Function<Tree, ?>> valueFunc = key -> {
        String fieldName = LambdaUtil.getFieldName(key);
        return tree -> tree.get(fieldName);
    };

    private final Map<String, Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>>> matcherMapKeyCache;

    private MultiTreeSearcher(List<Tree<T>> source) {
        super(source);
        matcherMapKeyCache = new HashMap<>(1);
    }

    public MultiTreeSearcher<T, R> eq(String key, Object value) {
        return conditionEq(true, defaultKey, tree -> tree.get(key), value);
    }

    public MultiTreeSearcher<T, R> like(String key, String value) {
        return conditionLike(true, defaultKey, tree -> tree.get(key).toString(), value);
    }

    public MultiTreeSearcher<T, R> eq(Func1<R, ?> key, Object value) {
        return conditionEq(true, defaultKey, valueFunc.apply(key), value);
    }

    public MultiTreeSearcher<T, R> like(Func1<R, String> key, String value) {
        return conditionLike(true, defaultKey, strValueFunc.apply(key), value);
    }

    public MultiTreeSearcher<T, R> eqSubTree(String treeKey, String key, Object value) {
        return conditionEq(true, getMatcherMapKey(treeKey), tree -> tree.get(key), value);
    }

    public MultiTreeSearcher<T, R> likeSubTree(String treeKey, String key, String value) {
        return conditionLike(true, getMatcherMapKey(treeKey), tree -> tree.get(key).toString(), value);
    }

    public MultiTreeSearcher<T, R> eq(boolean condition, String key, Object value) {
        return conditionEq(condition, defaultKey, tree -> tree.get(key), value);
    }

    public MultiTreeSearcher<T, R> like(boolean condition, String key, String value) {
        return conditionLike(condition, defaultKey, tree -> tree.get(key).toString(), value);
    }

    public MultiTreeSearcher<T, R> eq(boolean condition, Func1<R, ?> key, Object value) {
        return conditionEq(condition, defaultKey, valueFunc.apply(key), value);
    }

    public MultiTreeSearcher<T, R> like(boolean condition, Func1<R, String> key, String value) {
        return conditionLike(condition, defaultKey, strValueFunc.apply(key), value);
    }

    public MultiTreeSearcher<T, R> eqSubTree(boolean condition, String treeKey, String key, Object value) {
        return conditionEq(condition, getMatcherMapKey(treeKey), tree -> tree.get(key), value);
    }

    public MultiTreeSearcher<T, R> likeSubTree(boolean condition, String treeKey, String key, String value) {
        return conditionLike(condition, getMatcherMapKey(treeKey), tree -> tree.get(key).toString(), value);
    }


    private MultiTreeSearcher<T, R> conditionEq(boolean condition, Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> key, Function<Tree, ?> valueFunc, Object value) {
        if (condition) {
            return super.eq(key, valueFunc, value);
        }
        return typedThis;
    }

    private MultiTreeSearcher<T, R> conditionLike(boolean condition, Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> key, Function<Tree, String> valueFunc, String value) {
        if (condition) {
            return super.like(key, valueFunc, value);
        }
        return typedThis;
    }

    private final Function<String, Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>>>
            matcherMapKeyFunc = key -> Pair.of(tree -> (List<Tree>) tree.get(key), (tree, list) -> tree.putExtra(key, list));

    private synchronized Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> getMatcherMapKey(String key) {
        Pair<Function<Tree, List<Tree>>, BiConsumer<Tree, List<Tree>>> res = matcherMapKeyCache.get(key);
        if (Objects.isNull(res)) {
            res = matcherMapKeyFunc.apply(key);
            matcherMapKeyCache.put(key, res);
        }
        return res;
    }

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: hutool是一个Java工具类库,提供了丰富的工具类来方便Java开发人员快速开发,其中也包括了针对集合的工具类hutool集合工具类提供了丰富的API,包括对List、Queue、Stack、Map等集合数据结构的操作和处理。 其中,hutool提供的ListUtil工具类可以实现对List集合的排序、分页、去重等操作。QueueUtil和StackUtil工具类可以方便地对队列和栈进行push、pop等操作。MapUtil工具类则提供了map集合的常见操作,如put、get、remove等。 除了常见的集合类型,hutool还提供了TreeUtil工具类,可以方便的操作型结构的集合数据。 hutool集合工具类的设计追求简洁易用,内部实现也采用了高效的算法。并且,hutool集合工具类的代码开源,易于修改和扩展。 综上,hutool集合工具类可以大大提高Java开发人员的开发效率和代码质量,是一款不可多得的Java工具库。 ### 回答2: hutool是一个Java工具包,其中包含了许多实用工具类。在其中,集合工具类对于Java程序员来说相当重要。 hutool中的集合工具类提供了简单、高效、易用的方法来操作集合对象。例如,可以使用ListUtil、SetUtil、MapUtil工具类对List、Set、Map等集合进行快速操作,如转换、去重、排序、过滤、判空等。 其中,集合转换是hutool集合工具类的常见用途之一,可以将一个集合类型转换成另一种集合类型,比如将List转换成Set或Map,还可以指定Key和Value的转换器,让转换更加灵活和自由。同时,hutool集合工具类还提供了快捷的创建空集合和不可变集合的方法,非常方便,也能避免空指针异常等问题。 另外,hutool集合工具类还具有比较高的通用性,可以应用在各类Java程序开发中,涵盖了Spring、Mybatis、Hibernate等框架,还比较适用于处理大数据量的集合操作。 总的来说,hutool集合工具类是一款非常实用的Java工具包,对于开发人员来说是不可或缺的。它简化了集合操作的流程,提高了代码的编写效率和可读性。如果你还没有使用过hutool集合工具类,不妨尝试一下,它一定会给你的开发工作带来便利。 ### 回答3: Hutool是一个Java工具类库,它为Java开发人员提供了很多实用的工具类,其中就包括集合工具类Hutool集合工具类提供了一系列实用的方法,可以使Java开发人员更加便捷地操作集合,提升了开发效率。 Hutool集合工具类包含了对List、Set、Map等Java集合的操作。其中,对List操作的方法包括list转为数组、按照元素属性排序、去重、过滤等;对Set操作的方法包括set转为数组、去重、求交集、判断子集等;对Map操作的方法包括取最大值、取最小值、map转为List、根据value排序、根据value取key等。 Hutool集合工具类中也提供了一些特殊的数据结构,例如MultiValueMap,它可以将一个key对应多个value的情况存储在同一个Map中。同时,Hutool还提供了一些便捷的工具方法,例如采用Lambda表达式过滤集合、统计集合元素个数、集合元素分组等。 Hutool集合工具类的使用非常简单,只需导入相应的工具类包,即可使用其中的方法。例如,使用ListUtil中的distinct方法去重List可以直接调用ListUtil.distinct(list)即可。另外,Hutool集合工具类中的方法都提供了详细的文档说明,使开发人员更容易理解和使用。 总之,Hutool集合工具类提供了很多实用的方法,可以帮助Java开发人员更加便捷地操作集合,提升开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值