不使用递归,如何构造树结构
原理很简单,利用对象引用特性。
科普一下知识点:
浅拷贝:
浅拷贝又称为浅复制,浅克隆,浅拷贝是指拷贝时只拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用所指向的对象,拷贝出来的对象的所有变量的值都含有与原来对象相同的值,而所有对其他对象的引用都指向原来的对象,简单地说,浅拷贝只拷贝对象不拷贝引用。
深拷贝:
深拷贝又称为深复制,深克隆,深拷贝不仅拷贝对象本身,而且还拷贝对象包含的引用所指向的对象,拷贝出来的对象的所有变量(不包含那些引用其他对象的变量)的值都含有与原来对象的相同的值,那些引用其他对象的变量将指向新复制出来的新对象,而不指向原来的对象,简单地说,深拷贝不仅拷贝对象,而且还拷贝对象包含的引用所指向的对象。
思路:
在构建树形结构时,我们最常用方法是使用递归算法进行处理,让程序按照我们的想法一步一步的向下查找子节点,这个过程是程序员通过代码控制的;
参考对象引用的特性,这个过程其实完全可以利用引用特性自动执行;
进入正题:
第一步:判断实体中包含 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 }