不使用递归,如何构造树结构

原理很简单,利用对象引用特性。

科普一下知识点:

浅拷贝:
浅拷贝又称为浅复制,浅克隆,浅拷贝是指拷贝时只拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用所指向的对象,拷贝出来的对象的所有变量的值都含有与原来对象相同的值,而所有对其他对象的引用都指向原来的对象,简单地说,浅拷贝只拷贝对象不拷贝引用。

深拷贝:
深拷贝又称为深复制,深克隆,深拷贝不仅拷贝对象本身,而且还拷贝对象包含的引用所指向的对象,拷贝出来的对象的所有变量(不包含那些引用其他对象的变量)的值都含有与原来对象的相同的值,那些引用其他对象的变量将指向新复制出来的新对象,而不指向原来的对象,简单地说,深拷贝不仅拷贝对象,而且还拷贝对象包含的引用所指向的对象。

思路:

在构建树形结构时,我们最常用方法是使用递归算法进行处理,让程序按照我们的想法一步一步的向下查找子节点,这个过程是程序员通过代码控制的;

参考对象引用的特性,这个过程其实完全可以利用引用特性自动执行;

进入正题:

第一步:判断实体中包含 id parentId childList这三个构建一颗树的必备属性;

第二步:查找到每一列数据的下一级元素;

第三步:记录所有的 id,用于筛选出来第一级的节点,一个简单的思路,如果 parentId  不存在于 ids数组中,那么当前节点一定是一级节点;

第四步:将一级节点加入新数组,并返回;

直接上代码(效率比较低):

 1 public <E extends Object> List<E> tree(List<E> e) {
 2         List<E> result = new ArrayList<>();
 3         List<Long> ids = new ArrayList<>();
 4         for (E e1 : e) {
 5             Method setChildList = e1.getClass().getMethod("setChildList",List.class);
 6             if(ObjectUtils.isEmpty(setChildList)) continue;
 7             Method getId = e1.getClass().getMethod("getId");
 8             if(ObjectUtils.isEmpty(getId)) continue;
 9             long id = (long) getId.invoke(e1);
10             if(ObjectUtils.isEmpty(id)) continue;
11             Method getParentId = e1.getClass().getMethod("getParentId");
12             if(ObjectUtils.isEmpty(getParentId)) continue;
13             long parentId = (long) getParentId.invoke(e1);
14             if(ObjectUtils.isEmpty(parentId)) continue;
15             ids.add(id);
16             List<E> es = e.stream().filter(p -> {
17                 try {
18                     Method pk = p.getClass().getMethod("getParentId");
19                     if (ObjectUtils.isEmpty(pk)) return false;
20                     long pv = (long) pk.invoke(p);
21                     if (ObjectUtils.isEmpty(pv)) return false;
22                     return pv == id;
23                 } catch (Throwable ex) {
24                     return false;
25                 }
26             }).collect(Collectors.toList());
27             if(!ObjectUtils.isEmpty(es)) setChildList.invoke(e1,es);
28         }
29         for (E e1 : e) {
30             Method getParentId = e1.getClass().getMethod("getParentId");
31             if(ObjectUtils.isEmpty(getParentId)) continue;
32             long parentId = (long) getParentId.invoke(e1);
33             if(ObjectUtils.isEmpty(parentId)) continue;
34             if(!ids.contains(parentId)) result.add(e1);
35         }
36 
37         return result;
38     }

 优化一波,代码如下(提前构建一个MAP,替代原来的二次循环查找,实测效率提升了几十倍):

 1 /**
 2      * 利用bean引用原理构建树
 3      * @param e
 4      * @param <E>
 5      * @return
 6      */
 7     @SneakyThrows
 8     public <E extends Object> List<E> listTotree(List<E> e) {
 9         long millis = System.currentTimeMillis();
10         List<E> result = new ArrayList<>();
11         List<String> ids = new ArrayList<>();
12         Map<String,E> emap = new HashMap<>();
13         //生产Map
14         e.stream().sequential().forEach(e1->{
15             try {
16                 Method setChildList = e1.getClass().getMethod("setChildList", List.class);
17                 if(ObjectUtils.isEmpty(setChildList)) return;
18                 Method getId = null;
19                 try{
20                     getId = e1.getClass().getMethod("getId");
21                 }catch (Throwable te) {
22                     getId = e1.getClass().getMethod("getOrganizationId");
23                 }
24                 if(ObjectUtils.isEmpty(getId)) return;
25                 String id = getId.invoke(e1)+"";
26                 if(ObjectUtils.isEmpty(id)) return;
27                 emap.put(id,e1);
28                 ids.add(id);
29             } catch (Throwable ex) {
30                 ex.printStackTrace();
31             }
32         });
33         //注入子集
34         e.stream().sequential().forEach(e1->{
35             try {
36                 Method setChildList = e1.getClass().getMethod("setChildList", List.class);
37                 if(ObjectUtils.isEmpty(setChildList)) return;
38                 Method getId = null;
39                 try{
40                     getId = e1.getClass().getMethod("getId");
41                 }catch (Throwable te) {
42                     getId = e1.getClass().getMethod("getOrganizationId");
43                 }
44                 if(ObjectUtils.isEmpty(getId)) return;
45                 String id = getId.invoke(e1)+"";
46                 if(ObjectUtils.isEmpty(id)) return;
47                 Method getParentId = e1.getClass().getMethod("getParentId");
48                 if(ObjectUtils.isEmpty(getParentId)) return;
49                 String parentId = getParentId.invoke(e1)+"";
50                 if (ObjectUtils.isEmpty(parentId)) return;
51                 E e2 = emap.get(parentId);
52                 if(ObjectUtils.isEmpty(e2)) return;
53                 Method setChild = e2.getClass().getMethod("setChildList",List.class);
54                 if(ObjectUtils.isEmpty(setChild)) return;
55                 Method getChild = e2.getClass().getMethod("getChildList");
56                 if(ObjectUtils.isEmpty(getChild)) return;
57                 Object child = getChild.invoke(e2);
58                 if(ObjectUtils.isEmpty(child)) setChild.invoke(e2,new ArrayList<E>());
59                 ((Collection<E>)getChild.invoke(e2)).add(e1);
60             } catch (Throwable ex) {
61                 ex.printStackTrace();
62             }
63         });
64         //过滤顶层
65         e.stream().parallel().forEach(e1->{
66             Method getParentId = null;
67             try {
68                 getParentId = e1.getClass().getMethod("getParentId");
69                 if(!ObjectUtils.isEmpty(getParentId)) {
70                     String parentId = getParentId.invoke(e1)+"";
71                     Method getId = null;
72                     try{
73                         getId = e1.getClass().getMethod("getId");
74                     }catch (Throwable te) {
75                         getId = e1.getClass().getMethod("getOrganizationId");
76                     }
77                     String id = getId.invoke(e1)+"";
78                     if(
79                             ObjectUtils.isEmpty(parentId)
80                                     ||!ids.contains(parentId)
81 //                                    || parentId.equals(id)
82                     ) result.add(e1);
83                 }
84             } catch (Throwable ex) {
85                 ex.printStackTrace();
86             }
87         });
88         log.info("列表转树 耗时{}ms",(System.currentTimeMillis()-millis));
89         return result;
90     }

 

posted on 2023-11-10 16:39  instr  阅读(654)  评论(1编辑  收藏  举报

导航