开发用工具类:无侵入式的树形状工具类TreeUtils

这是处理树形状工具类的工具类,只需要jdk8(现在jdk18都出了,好吧)就可以复制进去用。

我看到的其他TreeUtils太粗糙难用(必须是id和parentId,或者对象实现自定义排序接口,再调用工具类?),就自己写了一个简单基于字符串处理的工具类。非字符串的,可以自己参考,改进。

 

 代码如下:

package cn.jow.extra.tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 无侵入方式生成新的树形状结构数据
 * <pre>
 *     功能:
 *          1:对象数组或map数组 <==>树形状结构数组
 *          2:对象数组和map数组查找指定节点的顺序上层节点对象数组
 *          3:对象数组或map数组==>筛选指定属性的对象
 *     特点:
 *          1.不会重新创造对象
 *          2.不改变对象排序,若要排序,请先排序好再使用
 *          3.仅仅需要jdk8就可以运行,不需要引入其他包
 *          4.自测通过,效率不差。(三万条List<Map>数据的用时对比,下边0.038秒,对比递归遍历来源数组30.917秒,)
 *                            (三万条List<T>数据用时对比,下边0.119秒,对比递归遍历来源数组10.349秒)
 *          5.都是基于java.util的特定处理代码,不太复杂
 *     使用:
 *          1.实体对象数组使用的例子:TreeUtils.list2Tree(myObjectList,MyObject::getId,MyObject::getParentId,MyObject::getSonList)
 *          2.Map对象数组使用的例子:TreeUtils.list2Tree(mapList,"id","parentId","child")
 *     疑问:
 *          1.总数不对可能因为环形结构的节点被抛弃
 *          2.总数不对可能因为有父节点id但父节点不存在的节点会被抛弃
 *          3.完全不支持多线程处理同一个源数据数组
 *      说明:
 *          所见即所得,希望你觉得好用。良心若不痛,删掉作者信息,占为己用都不碍事。
 * </pre>
 *
 * @author JunOneWolf
 * @version 1.0
 * @date 2022-04-15
 */

public class TreeUtils {
    private TreeUtils() {
    }

    /**
     * 根据对象某个属性筛选部分对象
     *
     * @param srcArray  源对象
     * @param equalsStr 比较字符串
     * @param attribute 取值字段
     * @return 匹配的map数组
     */
    public static <T1, T2> List<Map<T1, T2>> findByEqualsAttribute(String equalsStr, List srcArray, String attribute) {
        List<Map<T1, T2>> result = new ArrayList<>();
        if (srcArray == null || srcArray.size() == 0 || isEmpty(equalsStr) || isEmpty(attribute)) {
            return result;
        }
        for (Object obj : srcArray) {
            Map t = (Map) obj;
            if (t != null && equalsStr.equals(t.get(attribute))) {
                result.add(t);
            }
        }
        return result;
    }

    /**
     * 根据对象某个属性筛选部分对象
     *
     * @param srcArray  源对象
     * @param equalsStr 比较字符串
     * @param attribute 取值字段
     * @param <T>       泛型
     * @return 匹配的字符串属性
     */
    public static <T> List<T> findByEqualsAttribute(String equalsStr, List<T> srcArray, Function<T, ? extends String> attribute) {
        List<T> result = new ArrayList<>();
        if (srcArray == null || srcArray.size() == 0 || isEmpty(equalsStr) || attribute == null) {
            return result;
        }
        for (T t : srcArray) {
            if (t != null && equalsStr.equals(attribute.apply(t))) {
                result.add(t);
            }
        }
        return result;
    }

    /**
     * 递归往上查找节点,可防止环形结构导致的无限制递归
     *
     * @param srcArray      单列表节点
     * @param equalsId      目标查找id
     * @param idField       查找的字段
     * @param parentIdField 父节点字段
     * @param <T>           泛型
     * @return 上层对象节点数组(父, 爷顺序往上)
     */
    public static <T> List<T> findParentListById(String equalsId, List<T> srcArray, Function<T, ? extends String> idField, Function<T, ? extends String> parentIdField) {
        List<T> result = new ArrayList<>();
        if (srcArray == null || srcArray.size() == 0 || idField == null || parentIdField == null) {
            return result;
        }
        Map<String, String> idAndParentIdMap = new HashMap<>(srcArray.size());
        Map<String, T> map = new HashMap<>(srcArray.size());
        for (T t : srcArray) {
            if (t != null) {
                String id = idField.apply(t);
                String parentId = parentIdField.apply(t);
                if (!isEmpty(id)) {
                    map.put(id, t);
                    if (!isEmpty(parentId)) {
                        idAndParentIdMap.put(id, parentId);
                    }
                }
            }
        }
        List<String> userIdList = new ArrayList<>();
        findParentListByMap(userIdList, equalsId, idAndParentIdMap);
        userIdList.forEach(s -> result.add(map.get(s)));
        return result;
    }

    /**
     * 递归往上查找节点,可防止环形结构导致的无限制递归
     *
     * @param srcArray      单列表节点
     * @param idObj         目标查找id
     * @param idField       查找的字段
     * @param parentIdField 父节点字段
     * @return 顺序的上层对象节点数组
     */
    public static <T1, T2> List<Map<T1, T2>> findParentListById(Object idObj, List srcArray, String idField, String parentIdField) {
        List<Map<T1, T2>> result = new ArrayList<>();
        if (srcArray == null || srcArray.size() == 0 || idObj == null || isEmpty(idField) || isEmpty(parentIdField)) {
            return result;
        }
        Map<String, String> idAndParentIdMap = new HashMap<>(srcArray.size());
        Map<String, Map<T1, T2>> map = new HashMap<>(srcArray.size());

        for (Object obj : srcArray) {
            if (obj != null) {
                Map t = (Map) obj;
                Object id = t.get(idField);
                Object parentId = t.get(parentIdField);
                if (!isEmpty(id)) {
                    map.put(id.toString(), t);
                    if (!isEmpty(parentId)) {
                        idAndParentIdMap.put(id.toString(), parentId.toString());
                    }
                }
            }
        }
        String equalsId = idObj.toString();
        List<String> userIdList = new ArrayList<>();
        findParentListByMap(userIdList, equalsId, idAndParentIdMap);
        userIdList.forEach(s -> result.add(map.get(s)));
        return result;
    }

    /**
     * 树形状结构转化为数组
     *
     * @param srcList    来源数组
     * @param childField 属性字段
     * @param <T>        泛型
     * @return 对象数组
     */
    public static <T> List<T> tree2List(List<T> srcList, Function<T, ? extends List<T>> childField) {
        if (srcList == null || srcList.size() == 0 || childField == null) {
            return srcList;
        } else {
            List<T> resultList = new ArrayList<>(srcList.size());
            for (T t : srcList) {
                findAllSonByChildField(t, resultList, childField);
            }
            return resultList;
        }
    }

    /**
     * 树形状转化单一列表
     *
     * @param srcList    来源对象
     * @param childField 子节点对象
     * @param <T1>       泛型
     * @param <T2>       泛型
     * @return 单列表数组
     */
    public static <T1, T2> List<Map<T1, T2>> tree2List(List srcList, String childField) {
        if (srcList == null || srcList.size() == 0 || isEmpty(childField)) {
            return srcList;
        } else {
            List<Map<T1, T2>> resultList = new ArrayList<>(srcList.size());
            for (Object obj : srcList) {
                Map t = (Map) obj;
                findAllSonByChildField(t, resultList, childField);
            }
            return resultList;
        }
    }

    /**
     * 生成树形状结构数据
     *
     * @param srcList      对象数组
     * @param idName       当前节点标识
     * @param parentIdName 上层节点标识
     * @param childName    存储的子节点数组
     * @return 目标对象
     */
    public static <T1, T2> List<Map<T1, T2>> list2Tree(List srcList, String idName, String parentIdName, String childName) {
        if (srcList == null || srcList.size() == 0 || isEmpty(idName) || isEmpty(parentIdName) || isEmpty(childName)) {
            return srcList;
        } else {
            if (idName.equals(parentIdName) || idName.equals(childName) || parentIdName.equals(childName)) {
                throw new RuntimeException("编码异常,三个字段不能任意相等");
            }
            List<Map<T1, T2>> resultList = new ArrayList<>();
            Map<String, List<Map<T1, T2>>> parentIdAndObjListMap = new HashMap<>(srcList.size());
            for (Object obj : srcList) {
                Map<T1, T2> map = (Map<T1, T2>) obj;
                if (map == null || isEmpty(map.get(idName))) {
                    continue;
                }
                if (isEmpty(map.get(parentIdName))) {
                    resultList.add(map);
                } else {
                    List<Map<T1, T2>> tempList = parentIdAndObjListMap.computeIfAbsent(map.get(parentIdName).toString(), k -> new ArrayList<>());
                    tempList.add(map);
                }
            }
            for (Map<T1, T2> s : resultList) {
                insertSonList2ChildAttribute(s, parentIdAndObjListMap, idName, childName);
            }
            return resultList;
        }

    }

    /**
     * 生成树形状数组
     *
     * @param srcList       源数组
     * @param idField       字段
     * @param parentIdField 父字段
     * @param childField    字节点的存储位置
     * @param <T>           泛型
     * @return 新的结构后数据数组
     */
    public static <T> List<T> list2Tree(List<T> srcList, Function<T, ? extends String> idField, Function<T, ? extends String> parentIdField, Function<T, ? extends List<T>> childField) {
        if (srcList == null || srcList.size() == 0 || idField == null || parentIdField == null || childField == null) {
            return srcList;
        } else {
            List<T> resultList = new ArrayList<>(srcList.size());
            Map<String, List<T>> parentIdAndObjListMap = new HashMap<>(srcList.size());
            for (T t : srcList) {
                if (t == null || isEmpty(idField.apply(t))) {
                    continue;
                }
                if (childField.apply(t) == null) {
                    throw new RuntimeException("编码异常,实体必须初始化,比如:private List child = new ArrayList ();");
                }
                String parentId = parentIdField.apply(t);
                if (isEmpty(parentId)) {
                    resultList.add(t);
                } else {
                    List<T> tempList = parentIdAndObjListMap.computeIfAbsent(parentId, k -> new ArrayList<>());
                    tempList.add(t);
                }
            }
            resultList.forEach(s -> insertSonList2ChildAttribute(s, parentIdAndObjListMap, idField, childField));
            return resultList;
        }
    }

    private static <T> void insertSonList2ChildAttribute(T t, Map<String, List<T>> parentIdAndObjListMap, Function<T, ? extends String> idField, Function<T, ? extends List<T>> childField) {
        String id = idField.apply(t);
        List<T> tempObjList = parentIdAndObjListMap.get(id);
        if (tempObjList != null) {
            childField.apply(t).addAll(tempObjList);
            for (T tempObj : tempObjList) {
                insertSonList2ChildAttribute(tempObj, parentIdAndObjListMap, idField, childField);
            }
        }
    }

    private static void insertSonList2ChildAttribute(Map t, Map allList, String idName, String childName) {
        String id = t.get(idName).toString();
        List mapList = (List) allList.get(id);
        if (mapList != null) {
            t.put(childName, mapList);
            for (Object o : mapList) {
                insertSonList2ChildAttribute((Map) o, allList, idName, childName);
            }
        }
    }

    private static boolean isEmpty(Object str) {
        return str == null || str.toString().length() == 0;
    }

    private static void findParentListByMap(List<String> userIdList, String id, Map<String, String> idAndParentIdMap) {
        if (idAndParentIdMap.containsKey(id)) {
            String parentId = idAndParentIdMap.get(id);
            //防止环形数据
            if (userIdList.contains(parentId)) {
                return;
            }
            userIdList.add(parentId);
            findParentListByMap(userIdList, parentId, idAndParentIdMap);
        }

    }

    private static <T> void findAllSonByChildField(T t, List<T> keepList, Function<T, ? extends List<T>> childField) {
        keepList.add(t);
        if (t == null) {
            return;
        }
        List<T> childSon = childField.apply(t);
        if (childSon != null) {
            for (T tempObject : childSon) {
                findAllSonByChildField(tempObject, keepList, childField);
            }
        }
    }

    private static void findAllSonByChildField(Map t, List keepList, String childField) {
        keepList.add(t);
        if (t == null) {
            return;
        }
        List<Map> childSon = (List<Map>) t.get(childField);
        if (childSon != null) {
            for (Map tempObject : childSon) {
                findAllSonByChildField(tempObject, keepList, childField);
            }
        }
    }


}

  

使用例子:

public static void main(String[] args) {
//lombok生成Get/Set方法(或者删除这两个标签,自己生成get/set)
@Getter
@Setter
class Money {
private String id;
private String value;
private List<Money> child = new ArrayList<>();

public Money(String id, String value) {
this.id = id;
this.value = value;
}

public String toString() {
return "Money{" +
"id='" + id + '\'' +
(value == null ? "" : (", value='" + value + '\'')) +
(child.size() > 0 ? child.toString() : "") +
'}';
}
}
List<Money> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int v = (int) (Math.random() * i);
list.add(new Money(String.valueOf(i), v == 0 ? null : String.valueOf(v)));
}
list.forEach(System.out::println);

System.out.println("-----处理树形状-----");
List<Money> tree = TreeUtils.list2Tree(list, Money::getId, Money::getValue, Money::getChild);
System.out.println(tree);
//随机生成的树结构数组的打印数据如下
//Money{id='0'}
//Money{id='1'}
//Money{id='2'}
//Money{id='3', value='2'}
//Money{id='4', value='1'}
//-----处理树形状-----
//[Money{id='0'}, Money{id='1'[Money{id='4', value='1'}]}, Money{id='2'[Money{id='3', value='2'}]}]

}

  

 

posted @ 2022-04-29 14:34  未名缺爱人  阅读(689)  评论(0)    收藏  举报