使用stream-peek实现递归树

前言

公司前端需要调用Cascader级联选择器功能,我这边要给他出一个递归接口。突然想到之前看的jdk8新特性中的peek,发现这个方法完美契合递归,就尝试用peek写了一个树形结构。下面具体讲一下这个功能。

peek

java 8 stream作为流式操作有两种操作类型,中间操作和终止操作。而peek就属于中间操作。咱们来看下面的例子

java 复制代码
Stream<String> stream = Stream.of("我", "是", "谁");
stream.peek(System.out::println);

这段代码本意是输出stream中的值,实际并没有任何输出。 中间操作是对数据的加工,注意,中间操作是lazy操作,并不会立马启动,需要等待终止操作才会执行。终止操作是stream的启动操作,只有加上终止操作,stream才会真正的开始执行。所以,peek是一个中间操作,所以上面的例子没有任何输出。

java 复制代码
Stream<String> stream = Stream.of("我", "是", "谁");
stream.peek(System.out::println).collect(Collectors.toList());

.collect(Collectors.toList()) 就是一个最终操作,可以正常输出结果。之后的事情就好办了(具体peek方法的使用还需要各位自己查询资料,这里不做过多赘述)。

递归

前提条件

需要jkd8及以上版本

实体类

java 复制代码
import lombok.Data;

import java.util.List;
@Data
public class TreeDto {
    /**
     * id
     */
    public Long id;
    /**
     * 名称
     */
    public String name;
    /**
     * 父id ,根节点为0
     */
    public Long pid;
    /**
     * 子节点信息
     */
    public List<TreeDto> childList;

    //构造函数
    public TreeDto(Long id, String name, Long pid, List<TreeDto> childList) {
        this.id = id;
        this.name = name;
        this.pid = pid;
        this.childList = childList;
    }
}

调用

java 复制代码
List<StaffFolder> staffFolders = staffFolderMapper.selectStaffFolderList(staffFolder);
List<TreeDto> treeDtos = staffFolders.stream()
        .map(result -> new TreeDto(result.getId(), result.getName(), result.getPid(), null))
        .collect(Collectors.toList());
List<TreeDto> collect = treeDtos.stream().filter(folder -> folder.getPid() == 0)
        .peek(folder -> folder.setChildList(getChildrens(folder, treeDtos)))
        .collect(Collectors.toList());

因一开始查询出的是StaffFolder数组,需要将其对应的字段参数赋值到TreeDto数组中,通过实体类中的构造函数进行赋值。 filter()筛选其中父类id为0的数据作为一级目录。然后在peek中进行子类赋值,因为peek作为中间操作,只有进行最终操作后才会输出,这就完美契合了递归的功能。

被调用

java 复制代码
private static List<TreeDto> getChildrens(TreeDto treeDto,List<TreeDto> TreeDtos){
    return TreeDtos.stream().filter(folder->Objects.equals(folder.getPid(),treeDto.getId()))
            .peek(folder->folder.setChildList(getChildrens(folder,TreeDtos)))
            .collect(Collectors.toList());
}

这里同理,将一级目录与数据传入,当父级id与id相同时,进行peek,之后循环调用自己return最终结果。

最终结果

java 复制代码
{
 "msg": null,
 "code": 1,
 "data": [
  {
   "id": 42,
   "name": "北京云梦智能科技有限公司",
   "pid": 0,
   "childList": [
    {
     "id": 49,
     "name": "客户文件",
     "pid": 42,
     "childList": []
    },
    {
     "id": 52,
     "name": "文件夹",
     "pid": 42,
     "childList": []
    }
   ]
  },
  {
   "id": 43,
   "name": "共享文件夹",
   "pid": 0,
   "childList": [
    {
     "id": 50,
     "name": " 客户资源",
     "pid": 43,
     "childList": []
    },
    {
     "id": 51,
     "name": "模板大纲",
     "pid": 43,
     "childList": []
    }
   ]
  },
  {
   "id": 48,
   "name": "公函文件夹",
   "pid": 0,
   "childList": []
  }
 ]
}

有时候前端希望"childList": []没有子级是返回null,当返回空数组时,前端会出现可以点击下级,而下级没有数据的情况。当然这个要看前端用的什么组件了。 我这举个简单的例子,扩展一下思路,各位有更好的方法就当我没说

java 复制代码
private static List<TreeDto> getChildrens(TreeDto treeDto,List<TreeDto> TreeDtos){
    return TreeDtos.stream().filter(folder->Objects.equals(folder.getPid(),treeDto.getId()))
            .peek(folder->folder.setChildList(getChildrens(folder,TreeDtos).size()>0?getChildrens(folder,TreeDtos):null))
            .collect(Collectors.toList());
}

通过简单的三目运算符,当返回值为空数组时,将null赋值。

扩展

功能

查询组织及下级组织人员数量

java 复制代码
   /**
     * 核算组织人数
     *
     * @param id
     * @return
     */
    public Long getPeopleNum(Long id) {
    	//上方遍历出了组织树
        List<OrganizationTreeDto> organizationTreeDtos = organizationTree(id);
        List<EmployeeEntity> list = new ArrayList<>();
        //总人数
        Long sumDepAllCount = 0L;
        if (organizationTreeDtos.size() > 0) {
            for (OrganizationTreeDto organizationTreeDto : organizationTreeDtos) {
            	//查询组织人员关联表
                LambdaQueryWrapper<EmployeeAndOrganizationEntity> entityLambdaQueryWrapper = new LambdaQueryWrapper<>();
                entityLambdaQueryWrapper.eq(EmployeeAndOrganizationEntity::getOrganizationId, organizationTreeDto.getId());
                List<EmployeeAndOrganizationEntity> employeeAndOrganizationEntities = employeeAndOrganizationMapper.selectList(entityLambdaQueryWrapper);
                //存在下级组织
                if (organizationTreeDto.getChildList().size() != 0) {
                	//递归返回人员数量
                    Long childListNum = getChildListNum(organizationTreeDto.getChildList(), 0L);
                    //加入总人数
                    sumDepAllCount = sumDepAllCount + childListNum;
                }
                //组织下有人员关联
                if (employeeAndOrganizationEntities.size()>0) {
                    for (EmployeeAndOrganizationEntity employeeAndOrganizationEntity : employeeAndOrganizationEntities) {
                        LambdaQueryWrapper<EmployeeEntity> employeeEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
                        employeeEntityLambdaQueryWrapper.eq(EmployeeEntity::getId, employeeAndOrganizationEntity.getEmployeeId());
                        Long aLong = employeeMapper.selectCount(employeeEntityLambdaQueryWrapper);
                        sumDepAllCount = sumDepAllCount + aLong;
                    }
                }
            }
        }
        return sumDepAllCount;
    }

    /**
     * 核算组织下级人数
     *
     * @param childList
     * @param sumDepAllCount
     * @return
     */
    private Long getChildListNum(List<OrganizationTreeDto> childList, Long sumDepAllCount) {
        for (OrganizationTreeDto organizationTreeDto : childList) {
            LambdaQueryWrapper<EmployeeAndOrganizationEntity> entityLambdaQueryWrapper = new LambdaQueryWrapper<>();
            entityLambdaQueryWrapper.eq(EmployeeAndOrganizationEntity::getOrganizationId, organizationTreeDto.getId());
                List<EmployeeAndOrganizationEntity> employeeAndOrganizationEntities = employeeAndOrganizationMapper.selectList(entityLambdaQueryWrapper);
            if (organizationTreeDto.getChildList().size() != 0) {
                getChildListNum(organizationTreeDto.getChildList(), sumDepAllCount);
            }
            if (employeeAndOrganizationEntities.size()>0) {
                for (EmployeeAndOrganizationEntity employeeAndOrganizationEntity : employeeAndOrganizationEntities) {
                    LambdaQueryWrapper<EmployeeEntity> employeeEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    employeeEntityLambdaQueryWrapper.eq(EmployeeEntity::getId, employeeAndOrganizationEntity.getEmployeeId());
                    Long aLong = employeeMapper.selectCount(employeeEntityLambdaQueryWrapper);
                    sumDepAllCount = sumDepAllCount + aLong;
                }
            }
        }
        return sumDepAllCount;
    }
相关推荐
AskHarries22 分钟前
Java字节码增强库ByteBuddy
java·后端
佳佳_36 分钟前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
许野平2 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
BiteCode_咬一口代码3 小时前
信息泄露!默认密码的危害,记一次网络安全研究
后端
齐 飞3 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
LunarCod4 小时前
WorkFlow源码剖析——Communicator之TCPServer(中)
后端·workflow·c/c++·网络框架·源码剖析·高性能高并发
码农派大星。4 小时前
Spring Boot 配置文件
java·spring boot·后端
杜杜的man5 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*5 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
llllinuuu5 小时前
Go语言结构体、方法与接口
开发语言·后端·golang