本例父子关系,子节点的parentId和父节点的id相同。
实现树形排列的方法
public static List<Type> buildTree(List<Type> treeNodes) {
List<Type> trees = new ArrayList<>();
for (Type treeNode : treeNodes) { // 第一层遍历,对每一个节点(目标)做一次父亲,找其孩子节点,进行封装
// 需要知道根节点的特征,遍历到根节点就可以封装到trees中(根节点就不会在有上层了),本例根节点parentId为空
if (StringUtils.isBlank(treeNode.getParentId())) {
trees.add(treeNode);
}
for (Type it : treeNodes) { // 第二层遍历全部,校验是否是(目标)的孩子节点,进行父子封装
if (Objects.equals(it.getParentId(), treeNode.getId())) {
if (treeNode.getChildList() == null) {
treeNode.setChildList(new ArrayList<Type>()); // 防止空指针
}
treeNode.getChildList().add(it);
}
}
}
return trees;
}
实现原理:
集合可以理解为对多个对象的引用。本例中入参为treeNodes集合。不管在后面的for循环还是add方法中,实际只是对trees(必须new出来,不能直接用treeNodes赋值)集合的组装,对于treeNodes集合而言,每一个对象的引用和顺序至始至终没有发生任何改变,改变的仅仅是值发生改变,每一个值都包含了childList对象。对于trees而言,只含有根节点数量个对象。
遇到的问题:
在调用处,获取trees集合的同时,传入的treeNodes也会发生多出childList的改变。如果不希望入参的treeNodes发生这个改变,就需要入参集合与treeNodes集合引用不同的对象地址。
错误的赋值引用
List list2 = new ArrayList();
list2.addAll(treeNodes);
当list2作为入参后,执行过方法发现treeNodes还是多了childList的属性。因为这种方法对list2而言是新的引用没错,此时对list2进行增删操作也不会影响treeNodes。但是加入的对象确不行,引用是相同的,当Type改变值的时候,在treeNodes和list2中都会发生改变。当然List list2 =treeNodes;就更不正确了
正确的赋值引用
需要不仅对list对象new出来,还需要对其中的每一个对象new出来一遍。
如引用代码:
List list3 = copyList(list1, Type::new);
具体方法:
public static <S, T> List<T> copyList(List<S> sources, Supplier<T> target) {
List<T> list = new ArrayList<>(sources.size());
for (S source : sources) {
T t = target.get();
try {
BeanUtils.copyProperties( t,source);
} catch (Exception e) {
e.printStackTrace();
}
list.add(t);
}
return list;
}
可以看到,前面虽然值一样,但是实际的引用地址已经不同了。
学海无涯苦作舟!!!