树状结构工具类
树结构实体通用工具类
因为在我遇到的开发过程中,树状结构的实体真的是有点多吧,出于每次构造子结构的时候,都要去写一个递归啥的进行遍历构造成树状结构觉得比较麻烦所以呢 我就自己采用注解的方式去实现了一个通用的树状结构工具类
- 当开始呢我采用的依旧是递归的方式去实现,发现当数据量大了,效率明显就很低了,而且我又采用了反射吧因为想写通用的所以还是采用了反射
- 当我完成第一个版本之后,我就想到了更好的解决办法就是采用引用的方式去构建树状结构,这样效率整整是递归方式的10倍吧,下面呢将要展示我的两种方式的构造.
实体
这里忽略一下规范性 因为只是测试使用
package com.linq.cool.entities;import com.linq.cool.tree.annotation.*;
import com.linq.cool.tree.enums.FieldType;
import lombok.Data;
import lombok.experimental.Accessors;import java.util.ArrayList;
import java.util.List;/*** @Author: yqlin* @Date: 2020/11/25 10:53* @Description: {* "id": "1",* "company_id": "8",* "area_code": "100000",* "area_name": "中国",* "area_level": "0",* "parent_area_code": "0",* "citycode": "",* "zipcode": "",* "mergername": "中国",* "lan": "116.3683244",* "lat": "39.915085",* "pinyin": "China"* },* @Version: 1.0.0*/
@TreeEntity
@Data
@Accessors(chain = true)
public class Area {private String id;private String company_id;@TreeField(fieldType = FieldType.ID)@TreeIdprivate String area_code;@TreeField(fieldType = FieldType.PID)@TreePidprivate String parent_area_code;private String area_name;private String area_level;private String citycode;private String zipcode;private String mergername;private String lan;private String lat;private String pinyin;@TreeField(fieldType = FieldType.CHILD)@TreeChildList<Area> children = new ArrayList<>();
}
递归方式
package com.linq.cool.tree;import com.linq.cool.reflect.ReflectUtils;
import com.linq.cool.tree.annotation.TreeChild;
import com.linq.cool.tree.annotation.TreeEntity;
import com.linq.cool.tree.annotation.TreeId;
import com.linq.cool.tree.annotation.TreePid;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;/*** @Author: yqlin* @Date: 2020/11/20 2:57 下午* @Description: 树结构实体通用工具类* @Version: 1.0.0*/
public final class TreeUtilV1 {private final static Logger logger = LoggerFactory.getLogger(TreeUtilV1.class);private final static Integer FIELD_SIZE = 3;/*** Map, String> ---> Map<[注解类型,[字段名]>*/ public static Map<Class<?>, String> fieldsMap = null;static {// 避免再次扩容 默认8fieldsMap = new HashMap<>(8);}/*** stream流递归写法** @param list 所有列表* @param pid 父级id* @param clazz 元素类型* @param 传入元素** @return 树状结构集合*/ public static <E> List<E> toTreeList(List<E> list, Object pid, Class<E> clazz) {if (pid instanceof String || pid instanceof Integer || pid instanceof Long) {if (fieldsMap.size() != FIELD_SIZE) {// ---->时间复杂度: O(n)fieldsMap = getFieldsMap(clazz);logger.info("调用完成 getFieldsMap:\n{}", fieldsMap);}// 获取父数据集合 并且 父赋值直接子数据集合return CollectionUtils.size(list) > 1 ? list.stream().filter(Objects::nonNull).filter(item -> ReflectUtils.invokeGetter(item, fieldsMap.get(TreePid.class)).equals(pid))// O(n^2).peek(child -> ReflectUtils.invokeSetter(child, fieldsMap.get(TreeChild.class), getChildList(ReflectUtils.invokeGetter(child, fieldsMap.get(TreeId.class)), list))).collect(Collectors.toList()) : list;}throw new RuntimeException("父级Id必须是[String,Long,Integer]其中一种");}/*** 获取子元素集合** @param id 主id* @param list 列表集合* @param 传入元素** @return 树状结构集合*/ private static <E> List<E> getChildList(Object id, List<E> list) {// 子数据集合的直接子对象 并且 子数据集合 间接赋值return CollectionUtils.size(list) > 1 ? list.stream().filter(Objects::nonNull).filter(item -> ReflectUtils.invokeGetter(item, fieldsMap.get(TreePid.class)).equals(id)).peek(child -> ReflectUtils.invokeSetter(child, fieldsMap.get(TreeChild.class), getChildList(ReflectUtils.invokeGetter(child, fieldsMap.get(TreeId.class)), list))).collect(Collectors.toList()) : list;}/*** 获取 Field Map** @param clazz 类名** @return Map, String> ---> Map<[注解类型,[字段名]>*/ private static <E> Map<Class<?>, String> getFieldsMap(Class<E> clazz) {// 获取树状结构实体TreeEntity treeEntity = clazz.getAnnotation(TreeEntity.class);// 判断注解是否为空if (treeEntity == null) {throw new RuntimeException("该实体类不是树状实体");}Field[] declaredFields = clazz.getDeclaredFields();for (Field field : declaredFields) {if (field.getAnnotation(TreeId.class) != null) {fieldsMap.put(TreeId.class, field.getName());}if (field.getAnnotation(TreePid.class) != null) {fieldsMap.put(TreePid.class, field.getName());}if (field.getAnnotation(TreeChild.class) != null) {fieldsMap.put(TreeChild.class, field.getName());}}if (fieldsMap.size() < FIELD_SIZE) {throw new RuntimeException("缺少(@TreeId|@TreePid|@TreeChild)其中一个注解");}return fieldsMap;}
}
引用方式
package com.linq.cool.tree;import com.linq.cool.reflect.ReflectUtils;
import com.linq.cool.tree.annotation.TreeChild;
import com.linq.cool.tree.annotation.TreeEntity;
import com.linq.cool.tree.annotation.TreeId;
import com.linq.cool.tree.annotation.TreePid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.Field;
import java.util.*;/*** @Author: yqlin* @Date: 2020/11/20 2:57 下午* @Description: 树结构实体通用工具类* @Version: 1.0.0*/
public final class TreeUtilV3 {private final static Logger logger = LoggerFactory.getLogger(TreeUtilV3.class);private final static Integer FIELD_SIZE = 6 >> 1;/*** Map, String> ---> Map<[注解属性类型,[字段名]>* 默认情况下,HashMap容量为16,加载因子为0.75,即当HashMap中的数据量达到 16 X 0.75 = 12 时,将触发扩容操作。* 现在是3个进入map 触发条件容量4 所以初始化8 就可以避免触发再次扩容*/ public static Map<Class<?>, String> fieldsMap = new HashMap<>(8);/*** @param list 所有列表* @param rootNode 根节点* @param clazz 元素类型* @param 传入元素** @return 树状结构实体*/ public static <E> E toTree(List<E> list, E rootNode, Class<E> clazz) {if (fieldsMap.size() != FIELD_SIZE) {// ---->时间复杂度: O(n)fieldsMap = getFieldsMap(clazz);logger.info("调用完成 getFieldsMap:\n{}", fieldsMap);}HashMap<Object, E> map = new HashMap<>(2);Object id = ReflectUtils.invokeGetter(rootNode, fieldsMap.get(TreeId.class));Object pid = ReflectUtils.invokeGetter(rootNode, fieldsMap.get(TreePid.class));map.put(id, rootNode);for (E childNode : list) {Object tId = ReflectUtils.invokeGetter(childNode, fieldsMap.get(TreeId.class));map.put(tId, childNode);Object tPid = ReflectUtils.invokeGetter(childNode, fieldsMap.get(TreePid.class));if (!pid.equals(tPid)) {//父节点E parentNode = map.get(tPid);//给父节点的child属性赋当前节点List<E> tChild = ReflectUtils.invokeGetter(parentNode, fieldsMap.get(TreeChild.class));if (tChild != null) {tChild.add(childNode);ReflectUtils.invokeSetter(parentNode, fieldsMap.get(TreeChild.class), tChild);}}}return map.get(id);}/*** 引用写法 绕过递归** @param list 所有列表* @param pid 父级id* @param clazz 元素类型* @param 传入元素** @return 树状结构集合*/ public static <E> List<E> toTreeList(List<E> list, Object pid, Class<E> clazz) {if (pid instanceof String || pid instanceof Integer || pid instanceof Long) {if (fieldsMap.size() != FIELD_SIZE) {// ---->时间复杂度: O(n)fieldsMap = getFieldsMap(clazz);logger.info("调用完成 getFieldsMap:\n{}", fieldsMap);}Map<Object, E> map = new HashMap<>(2);// 用来存放根节点List<E> rootNodes = new ArrayList<>();// ---->时间复杂度: O(n)for (E o : list) {Object tPid = ReflectUtils.invokeGetter(o, fieldsMap.get(TreePid.class));// 如果是父if (tPid.equals(pid)) {rootNodes.add(o);map.put(pid, o);}}// ---->时间复杂度: O(n)for (E childNode : list) {if (childNode != null) {Object tId = ReflectUtils.invokeGetter(childNode, fieldsMap.get(TreeId.class));map.put(tId, childNode);Object tPid = ReflectUtils.invokeGetter(childNode, fieldsMap.get(TreePid.class));if (!tPid.equals(pid)) {//父节点E parentNode = map.get(tPid);//给父节点的child属性赋当前节点List<E> tChild = ReflectUtils.invokeGetter(parentNode, fieldsMap.get(TreeChild.class));if (tChild != null) {tChild.add(childNode);ReflectUtils.invokeSetter(parentNode, fieldsMap.get(TreeChild.class), tChild);}}}}return rootNodes;}throw new RuntimeException("父级Id必须是[String,Long,Integer]其中一种");}/*** 获取 Field Map** @param clazz 类名** @return Map, String> ---> Map<[注解类型,[字段名]>*/ private static <E> Map<Class<?>, String> getFieldsMap(Class<E> clazz) {// 获取树状结构实体TreeEntity treeEntity = clazz.getAnnotation(TreeEntity.class);// 判断注解是否为空if (treeEntity == null) {throw new RuntimeException("该实体类不是树状实体");}Field[] declaredFields = clazz.getDeclaredFields();for (Field field : declaredFields) {if (field.getAnnotation(TreeId.class) != null) {fieldsMap.put(TreeId.class, field.getName());}if (field.getAnnotation(TreePid.class) != null) {fieldsMap.put(TreePid.class, field.getName());}if (field.getAnnotation(TreeChild.class) != null) {fieldsMap.put(TreeChild.class, field.getName());}}if (fieldsMap.size() < FIELD_SIZE) {throw new RuntimeException("缺少(@TreeId | @TreePid | @TreeChild )其中一个注解");}return fieldsMap;}
}
最后呢,具体的代码就去访问一下我的gitee地址吧:地址
记得给我一个star哦
最新的请入群: 995832569 备注:csdn
我相信大家好多都能用上的呢
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
