原代码:
private static JSONArray produceTree(List<Resource> resources,boolean needPermission){
JSONArray tree = new JSONArray();
if(resources!=null&&resources.size()>Number.ZERO) {//递归退出条件判断
for (Resource resource : resources) {
List<Resource> resourceList=resourceService.queryChildTreeByPid(resource.getId());//根据父id查询子资源列表
JSONArray children = produceTree(resourceList,needPermission,staffName,roleId);//递归,一直向下查找
JSONObject node=TreeUtil.spellTree(resource);//转换为具体的树形式
if(children!=null&&children.size()>Number.ZERO){
node.put("children",children);
}else if(needPermission){//需要该资源下对应的权限信息(最后一级,判断下面有没有权限关系)
node=toPermission(resource.getId(),node,staffName);
}
tree.add(node);
}
}
return tree;
}
Resource结构如下:
@Data
public class Resource {
private Integer id;//资源id
private String resourceName;//资源名称
private String url;//资源地址 可能无用
private String icon;//资源图标
private String clue;// 树形结构横向线索
private Integer distance; // 到根节点的距离 纵向线索
private Integer parentId;// 父资源id
private String description; //资源描述
private Date createtime;//创建时间
private Integer state;// 0未删除 1已删除
List<Resource> children;
}
现在想用java8 Stream流重写这段代码,大概是这样:
private static List<Resource> recursive(List<Resource> resources){
return resources.stream()
.map(Resource::getId)//第一步转换为id
.map(parentId->resourceService.queryChildTreeByPid(parentId))//第二步查找id下对应的资源(id相当于parentId使用)
.map(child->Optional.ofNullable(child).filter(obj -> true).ifPresent(isExists->recursive(isExists)))//第三步 如果childrenList不为null,递归此方法
.toArray(childrenL->reReousce.setChildren());//第四步 将得到的List<Resource> 收集到上一个resource的children中 , resource.setChildren(childrenList)
}
第三步以后 编译出错
设想一下 第三步应该用toArray方法收集子资源 然后第四步将收集到的子资源放进对应的父资源resource中 请问如何实现呢
--------------感谢imanguo大佬的指点 还有某me姓大佬的指点 分享下我的树形结构生产源码-------
--------------Any library suggestions/code samples/guidance would be greatly appreciated...--------------
private static ResourceService resourceService;
private static PermissionService permissionService;
@Autowired
public TreeUtil(ResourceService resourceService,PermissionService permissionService){
this.resourceService=resourceService;
this.permissionService=permissionService;
}
/**
* @Author: zms
* @Description: 生成菜单树结构
*
* clue是线索 例如 1-2-3 代表 根节点是1 1下面有2节点 2下面有3节点
* 根据收集根节点id 作为线索 可以去除持久化数据里面重复的资源
* 例如 同时有了 1节点 2节点 因为1是2的父节点 那么有了1就不需要2的存在了 有父节点就拥有其所有子节点
*
* @Date: Create on 2018/11/12 14:59
*/
public static List<Resource> produceTree(List<Resource> resources,boolean needPermission){
Map<Boolean,List<Resource>> listMap=partitionResource(resources);
List<Resource> nodeTree=listMap.get(true);//根节点资源树
List<Resource> duplicateChildTree=listMap.get(false);//非根节点
String clue=nodeTree.stream().map(Resource::getId).map(String::valueOf).collect(joining("-","","-"));//根节点线索
List<Resource> childTree=duplicateChildTree.stream().filter(resource -> !clue.contains(resource.getClue())).collect(toList());//去除重复资源
nodeTree.addAll(childTree);//合并所有
List<Resource> downTree=TreeUtil.recursiveDown(nodeTree,needPermission);//向下递归查找子节点
List<Resource> upTree=TreeUtil.recursiveUp(downTree);//向上递归查找父节点
return merge(upTree);
}
/**
* @Author: zms
* @Description: 新递归生成树方法 上至下
*
* 5 ---
* 2 --- 6 ---
* 1--- 3 --- 7 ---
* 4 --- 8 ---
* 9 ---
*
* 以1开始查找到2,3,4 然后2,3,4再次递归 2(5,6) 3(7) 4(8,9) 括号内是对应的children
* 为什么引入ResourceGroup? 将resource和ChildrenResource 向上抽象一层
* Resource中含有其他属性 ResourceGroup仅仅表示父子节点的关系 可以不使用 看个人习惯
* @Date: Create on 2018/11/8 11:30
*/
public static List<Resource> recursiveDown(List<Resource> resources, boolean needPermission){
return resources
.parallelStream()
.map(resource -> TreeUtil.toResourceGroup(resource,needPermission))
.peek(ResourceGroup::autoSet)
.map(ResourceGroup::getParent)
.collect(Collectors.toList());
}
/**
* @Author: zms
* @Description: 转换成resouceGroup收集
* @Date: Create on 2018/11/8 11:30
*/
private static ResourceGroup toResourceGroup(Resource resource, boolean needPermission) {
ResourceGroup resourceGroup=new ResourceGroup();
resourceGroup.setParent(resource);
List<Resource> children = findChildren(resource.getId(),needPermission);
if(Objects.equals(0,children.size())&&needPermission){
List<Permission> permissions=permissionService.getPermissionByResourceId(resource.getId());
resourceGroup.setPermissions(permissions);
return resourceGroup;
}
resourceGroup.setChildren(children);
return resourceGroup;
}
/**
* @Author: zms
* @Description: 获取父节点对应的子资源
* @Date: Create on 2018/11/8 11:30
*/
private static List<Resource> findChildren(Integer parentId,boolean needPermission){
List<Resource> chidren=resourceService.queryChildTreeByPid(parentId);
recursiveDown(chidren,needPermission);
return chidren;
}
/**
* @Author: zms
* @Description: 新递归生成树方法 下至上
* @Date: Create on 2018/11/8 18:32
*/
private static List<Resource> recursiveUp(List<Resource> resources){
return resources.stream()
.map(TreeUtil::toResource)
.map(ResourceGroup::getParent)
.collect(Collectors.toList());
}
/**
*@Author: zms
*@Description: 递归查找父节点
*@Date: Create On 2018/11/10 14:33
*/
private static ResourceGroup toResource(Resource resource){
ResourceGroup resourceGroup=new ResourceGroup();
Resource parent=findParent(resource);
resourceGroup.setParent(parent);
return resourceGroup;
}
/**
* @Author: zms
* @Description: 获取收集好的子资源的父节点
* @Date: Create on 2018/11/8 18:37
*/
private static Resource findParent(Resource resource){
if(Objects.equals(0,resource.getParentId())){
return resource;
}
Resource parent=resourceService.getResourceById(resource.getParentId());
List<Resource> children=new ArrayList<>();
children.add(resource);
parent.setChildren(children);
return recursiveUp(Collections.singletonList(parent)).stream().filter(Objects::nonNull).findFirst().orElse(resource);
}
/**
* @Author: zms
* @Description:
*
* 业务背景: 假设id为1的资源节点下有 2,3,4 三个子资源 可能某个角色只有 2,3的资源树
* 2 -->1
* dulipcate
* 3 -->1
* 这时需要把2 3 合并到 1 下
* @Date: Create on 2018/11/12 11:44
*/
private static List<Resource> merge(List<Resource> duplicateTree){
return duplicateTree
.stream()
.collect(toMap(Resource::getId, Function.identity(),TreeUtil::mergeChildResource))
.entrySet()
.stream()
.map(Map.Entry::getValue)
.collect(Collectors.toList());
}
/**
* @Author: zms
* @Description:
*
* id:1 id:1 id:1
* children:[ children:[ children:[
* { { {
* id=2 id=3 合并为 id=2
* } } },
* ] ] {
* id=3
* }
*
* @Date: Create on 2018/11/12 12:00
*/
private static Resource mergeChildResource(Resource resource1,Resource resource2){
resource1.getChildren().add(resource2.getChildren().get(0));
return resource1;
}
/**
* @Author: zms
* @Description: 根节点与不是根节点的resource分组
* @Date: Create on 2018/11/12 15:01
*/
private static Map<Boolean,List<Resource>> partitionResource(List<Resource> resources){
return resources.stream().collect(partitioningBy(resource-> Objects.equals(0,resource.getParentId())));
}
/**
* @Author: zms
* @Description: 去重方法
* @Date: Create on 2018/8/10 10:13
*/
public static String removeDuplicateString(String str){
return Stream.of(str.split(",")).distinct().collect(Collectors.joining(","));
}
emmm,我拿下来了代码,不过编译有各种错误,我就没有管源代码的事,所以这个回答可能最终无法给你直接可以使用的答案代码,只有一个思路了哈
根据你最后提问这个,结合你代码,简单看了哈你想要达到的效果
应该是,有一堆节点
List<Resource>
,遍历它们,根据其中的id
属性作为条件查询父属性parentId
值与之相当的子节点List<Resource>
,最后可能要对子节点做一些处理后再放到刚父节点的children
属性整个过程看来,个人感觉一个比较肯定的答案是:这个过程不能通过一套的
stream
完成,也就是不能通过你写的这种一套完成下去为啥这么说呢,
stream
本身要处理的场景也就是不断的数据转换映射和过滤的过程,stream
里包含的数据也是在这个过程中不断变化的,所以这里有个关键点就是,本身按照父Resource
是能转换成子List<Resource>
,但是子List<Resource>
再次映射成其他的时候,stream
里的数据已经不再有父Resource
了,所以你不能再用之前stream
里的数据再来做set
动作结合你的代码再来看看(写
stream
操作的时候,一定随时要注意stream
里当前的数据到底是什么,才不会写错)此时根据
resources
生成流,此时是Stream<Resource>
用了
map
操作,这就是映射转换操作,此时已经变成Stream<Long>
,其实这一步已经意味着你之后不能再用Resource.setChildren
了,因为此时流里已经是Long
,是其资源id
了,后面就算能用id
再转换成子节点List<Resource>
也无力回天了当然并不是说没法弄了,明白了
stream
的这些性质,也还是可以写的,不过最终写法可能用不到太多stream
语法因为最终是需要再用到之前的父
Resource
的,所以要保留之前的父Resource
,所以可以尝试把循环父Resource
和找到其子Resource
,并最终set
回父Resource
,这三个过程分开写这只是按照你后面写的代码来改造的写法,有两点
stream
的写法,不过业务决定代码(不强制套任何结构或者框架,找到属于业务最合适,最清楚的代码就是最好的)以上仅供参考哈
===================================小做更改==================================
根据刚才的评论,想起之前不知道谁说的话:引用新的编程元素可以更加简单整理业务流程
这里新增一个
ResourceGroup
类来确定父与子之间的关系里面属性也就是一个父
Resource
和子List<Resource>
,这哈就可以直接用stream
完全表示了多了一个
toResourceGroup
方法其他的就是之前的方法,这样就可以在
stream
中补你还需要处理的方法了