飘乙己:List转Tree有4种写法!

简介: 孔乙己说:回字有四种写法。飘乙己也说,list转tree也有4种写法,你用的是哪种?

需求场景


有下面一张区域表,典型的树形结构设计。

9f0502a0394ce7716ab3b58c4138cf0b.png

现前端需要后端返回树形数据结构用于构造展示树。


cb85a1310aacdba72c1bab9d971223e9.png


本篇文章我们就来介绍一下在这种场景下后端构建树形数据结构,也就是通过list转tree的4种写法。

代码实战


  1. 首先我们根据数据库结构创建实体对象


/**
 * 区域平台
 * @author:Jam
 */
@Data
public class Platform {
    private String id;
    private String parentId;
    private String name;
    private String platformCode;
    private List<Platform> children;
    public Platform(String id, String platformCode,String parentId, String name) {
        this.id = id;
        this.parentId = parentId;
        this.name = name;
        this.platformCode = platformCode;
    }
}


  1. 为了便于演示我们就不连接数据库,而是直接使用Junit5的@BeforeEach注解初始化一份结构数据。


public class PlatformTest {
    private final List<Platform> platformList = Lists.newArrayList();
    private ObjectMapper objectMapper = new ObjectMapper();
    @BeforeEach
    private void init(){
        Platform platform0 = new Platform("1","001","0","集团");
        Platform platform1 = new Platform("2","QYPT001","1","销委会");
        Platform platform2 = new Platform("3","QYPT002","2","吉龙大区");
        Platform platform3 = new Platform("4","QYPT003","2","江苏大区");
        Platform platform4 = new Platform("5","QYPT004","4","南京分区");
        Platform platform5 = new Platform("6","QYPT005","1","教育BG");
        Platform platform6 = new Platform("7","QYPT006","6","华南大区");
        Platform platform7 = new Platform("8","QYPT007","6","华东大区");
        platformList.add(platform0);
        platformList.add(platform1);
        platformList.add(platform2);
        platformList.add(platform3);
        platformList.add(platform4);
        platformList.add(platform5);
        platformList.add(platform6);
        platformList.add(platform7);
    }
}


最无节操的写法


这种写法毫无节操可言,全部通过数据库递归查询。


首先查到根节点,parent_id = 0

通过根节点id获取到所有一级节点,parent_id = 1

递归获取所有节点的子节点,然后调用setChildren()方法组装数据结构。

这种写法我就不展示了,辣眼睛。都2021年了我见过不止一次在项目中出现这种写法。


双重循环


这种写法比较简单,也是比较容易想到的。通过双重循环确定父子节点的关系。


@SneakyThrows
@Test
public void test1(){
  System.out.println(platformList.size());
  List<Platform> result = Lists.newArrayList();
  for (Platform platform : platformList) {
    //获取根节点
    if(platform.getParentId().equals("0")){
      result.add(platform);
    }
    for(Platform child : platformList){
      if(child.getParentId().equals(platform.getId())){
        platform.addChild(child);
      }
    }
  }
  System.out.println(objectMapper.writeValueAsString(result));
}


同时需要给Platform添加一个addChild()的方法。


public void addChild(Platform platform){
  if(children == null){
    children = new ArrayList<>();
  }
  children.add(platform);
}


双重遍历


第一次遍历借助hashmap存储父节点与子节点的关系,第二次遍历设置子节点,由于map中已经维护好了对应关系所以只需要从map取即可。


@SneakyThrows
@Test
public void test2(){
  Map<String, List<Platform>> platformMap = new HashMap<>();
  platformList.forEach(platform -> {
    List<Platform> children = platformMap.getOrDefault(platform.getParentId(), new ArrayList<>());
    children.add(platform);
    platformMap.put(platform.getParentId(),children);
  });
  platformList.forEach(platform -> platform.setChildren(platformMap.get(platform.getId())));
  List<Platform> result = platformList.stream().filter(v -> v.getParentId().equals("0")).collect(Collectors.toList());
  System.out.println(objectMapper.writeValueAsString(result));
}


Stream 分组


@SneakyThrows
@Test
public void test4(){
  Map<String, List<Platform>> groupMap = platformList.stream().collect(Collectors.groupingBy(Platform::getParentId));
  platformList.forEach(platform -> platform.setChildren(groupMap.get(platform.getId())));
  List<Platform> collect = platformList.stream()
    .filter(platform -> platform.getParentId().equals("0")).collect(Collectors.toList());
  System.out.println(objectMapper.writeValueAsString(collect));
}


此处主要通过Collectors.groupingBy(Platform::getParentId)方法对platformList按照parentId进行分组,分组后父节点相同的都放一起了。


然后再循环platformList,给其设置children属性。


执行完成后已经形成了多颗树,最后我们再通过filter()方法挑选出根节点的那颗树即可。


目录
相关文章
|
7月前
|
安全 C#
C# List基本用法
C# List基本用法
|
6月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
4月前
|
JavaScript 前端开发 索引
JS中常用的数组迭代方法(filter,forEach,map,every,some,find,findIndex)
这段代码和说明介绍了JavaScript中数组的一些常用方法。函数接收三个参数:`item`(数组项的值)、`index`(项的位置,可选)和`array`(数组本身,可选)。示例展示了如何使用`filter()`过滤非空项、`forEach()`遍历数组、`map()`处理并返回新数组、`every()`检查所有元素是否满足条件、`some()`检查是否存在满足条件的元素、`find()`获取首个符合条件的元素值以及`findIndex()`获取其索引位置。这些方法都不会修改原数组。
JS中常用的数组迭代方法(filter,forEach,map,every,some,find,findIndex)
Leetcode 623. Add One Row to Tree
题目很简单,在树的第d层加一层,值为v。递归增加一层就好了。代码如下
53 0
|
存储 前端开发 数据库
飘乙己:List转Tree有4种写法!
飘乙己:List转Tree有4种写法!
112 1
|
JavaScript 索引
vue 数组处理= 循环--查找过滤--添加删除--去重[.every()/.some()/.find()/.filter()/.forEach()/.map()]
vue 数组处理= 循环--查找过滤--添加删除--去重[.every()/.some()/.find()/.filter()/.forEach()/.map()]
353 0
vue 数组处理= 循环--查找过滤--添加删除--去重[.every()/.some()/.find()/.filter()/.forEach()/.map()]
|
存储 Java 程序员
foreach中不允许对元素进行add和remove底层原理
foreach中不允许对元素进行add和remove底层原理 🍅 Java学习路线:搬砖工的Java学习路线 🍅 作者微信公众号:程序员小王 🍅 程序员小王的博客:https://www.wolai.com/wnaghengjie/ahNwvAUPG2Hb1Sy7Z8waaF 🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步 🍅 欢迎点赞 👍 收藏 ⭐留言 📝
133 0
foreach中不允许对元素进行add和remove底层原理
|
JavaScript 前端开发
Node.js:Array.prototype.sort数组排序的使用
Node.js:Array.prototype.sort数组排序的使用
148 0
|
Linux Windows
list删除数据的唯一正确写法
list删除数据的唯一正确写法
84 0
|
Java 数据库 Spring