樹結構物體通用工具類
因為在我遇到的開發程序中,樹狀結構的物體真的是有點多吧,出于每次構造子結構的時候,都要去寫一個遞回啥的進行遍歷構造成樹狀結構覺得比較麻煩所以呢 我就自己采用注解的方式去實作了一個通用的樹狀結構工具類
- 當開始呢我采用的依舊是遞回的方式去實作,發現當資料量大了,效率明顯就很低了,而且我又采用了反射吧因為想寫通用的所以還是采用了反射
- 當我完成第一個版本之后,我就想到了更好的解決辦法就是采用參考的方式去構建樹狀結構,這樣效率整整是遞回方式的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)
@TreeId
private String area_code;
@TreeField(fieldType = FieldType.PID)
@TreePid
private 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)
@TreeChild
List<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<Class<?>, String> ---> Map<[注解型別,[欄位名]>
*/
public static Map<Class<?>, String> fieldsMap = null;
static {
// 避免再次擴容 默認8
fieldsMap = new HashMap<>(8);
}
/**
* stream流遞回寫法
*
* @param list 所有串列
* @param pid 父級id
* @param clazz 元素型別
* @param <E> 傳入元素
*
* @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 <E> 傳入元素
*
* @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<Class < ?>, 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<Class<?>, 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 <E> 傳入元素
*
* @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 <E> 傳入元素
*
* @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<Class < ?>, 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哦
我相信大家好多都能用上的呢
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229839.html
標籤:java
上一篇:使用一維陣列,模擬堆疊資料結構
下一篇:Java 入門理解一下堆疊
