日常项目中经常会有将将带有父子结构的数据构造成树结构数据,比如组织机构数据。之前看到网上很多解决方案,发现构造成的树结构数据字段是封装好的某种特定的类,如果要添加其它返回字段,还需要自己在该类上添加字段。于是我在各位前辈的基础上,通过泛型实现了一套解决方案,并且支持自定义根节点判断。 需要转成树结构的类需要实现该接口,泛型T即为该实体类类型 public interface ITreeNode<T> { Object id(); Object parentId(); void addChild(T t); void removeChild(T t); List<T> children(); }
该函数式接口是用来判断根节点,有一个默认判断,当然也可以自定义实现。默认根节点判断方式为 parentId为null,或者parentId等于给定的值,给定的值可以传进来。
@FunctionalInterface public interface TreePredicate<T,R> { boolean test(T t, R r); static <T extends ITreeNode,R> TreePredicate<T ,R> rootNodePredicate(){ return (T t,R r)->t.parentId() == null || t.parentId().equals(r); } }
该类即为构造树结构的类,核心方法TreeBuilder(List<T> list, TreePredicate<T,R> predicate, R r)构造函数,核心思路就是引用传递
public class TreeBuilder<T extends ITreeNode<T>> { private final LinkedHashMap<Object, T> treeNodesMap = new LinkedHashMap<>(); private final List<T> rootNodesList = new ArrayList<>(); /** * @param list 平行关系的数据集合 * @param predicate 判断根节点 * @param r 和parentId比较的值,以此判断是否为根节点 * @param <R> 和parentId比较的值的类型 */ public <R> TreeBuilder(List<T> list, TreePredicate<T,R> predicate, R r){ for(T t : list){ treeNodesMap.put(t.id(), t); } treeNodesMap.values().forEach(v->{ if(!predicate.test(v,r)){ T p = treeNodesMap.get(v.parentId()); if(p != null){ p.addChild(v); } }else { rootNodesList.add(v); } }); } public <R> TreeBuilder(List<T> list, R r){ this(list,TreePredicate.rootNodePredicate(),r); } protected T getTreeNode(Object id) { return treeNodesMap.get(id); } public List<T> getRoot() { return rootNodesList; } public String treeJsonData(){ return JSONObject.toJSONString(rootNodesList); } /**获取某一节点所有子孙节点 * @param id 当前节点id * @return List<TreeNode> */ public List<T> getAllChildren(Object id) { List<T> allChildren = new ArrayList<>(16); T treeNode = getTreeNode(id); for (T t : treeNode.children()) { allChildren.add(t); allChildren.addAll(getAllChildren(t.id())); } return allChildren; } /**获取某一节点所有祖父节点 */ public List<T> getAllParent(Object id) { List<T> allParent = new ArrayList<>(16); T treeNode = getTreeNode(id); T parent = treeNodesMap.get(treeNode.parentId()); if(parent != null){ allParent.add(parent); allParent.addAll(getAllParent((parent.id()))); } return allParent; } }
简单测试类,根节点判断为空字符串
@Data public class OrgDTO implements ITreeNode<OrgDTO> { @JSONField(ordinal=1) private String id; @JSONField(ordinal=2) private String name; @JSONField(ordinal=3) private String parentId; @JSONField(ordinal=4) private List<OrgDTO> children = new ArrayList<>(); @Override public Object id() { return getId(); } @Override public Object parentId() { return getParentId(); } @Override public void addChild(OrgDTO orgDTO) { children.add(orgDTO); } @Override public List<OrgDTO> children() { return children; } @Override public void removeChild(OrgDTO orgDTO) { children.remove(orgDTO); } public OrgDTO(String id, String parentId, String name){ this.id = id; this.parentId = parentId; this.name = name; } public static void main(String[] args) { List<OrgDTO> list = new ArrayList<>(); list.add(new OrgDTO("1","","1")); list.add(new OrgDTO("2","1","2")); list.add(new OrgDTO("3","2","3")); list.add(new OrgDTO("4","3","4")); list.add(new OrgDTO("5","4","5")); TreeBuilder<OrgDTO> tree = new TreeBuilder<>(list,""); System.out.println(tree.treeJsonData()); } }