生成机构树 除了递归还有别的方法吗

2019-11-26 09:51:04 +08:00
 tubimasky

1w6 数据

如果从总公司开始 最多有 7 级

mybatis 自查询 /java 递归查数据库 大概 30s

java 全查 代码递归生成机构树 大概 10s

3986 次点击
所在节点    问与答
30 条回复
tubimasky
2019-11-26 16:59:18 +08:00
@aguesuka #18 转成 json 无限递归了...
egen
2019-11-26 17:17:39 +08:00
时间长是因为数据库查询太多吧,调整数据库结构可解
aguesuka
2019-11-26 17:18:06 +08:00
@tubimasky 因为 node 保存了父节点,你可以把序列化的时候忽略父节点字段。比如 fast json,@JSONField(serialize = false )
stevenkang
2019-11-26 17:22:21 +08:00
1w+ 的数据级别完全可以靠程序来遍历,全部查出来缓存到内存中再递归
johnny23
2019-11-26 19:51:40 +08:00
多做一个字段存所有父子关系的 id 列表
wuzhizuiguo
2019-12-23 09:35:46 +08:00
@aguesuka 谢谢, 有效,可以直接生成分类数,不过直接返回 root 太长,每个 chidren 里的 parent 都会显示出来,用 @JSONField(serialize = false )返回可以去掉, 不过感觉不太美观(是个 json 字符串,), 如果没有最高的父节点时,会报 root node more than one. 还有答主的 buildTree 方法,感觉很强,第一次看到这种写法, 唉,要学的还有好多啊.
aguesuka
2019-12-23 12:50:43 +08:00
@wuzhizuiguo 里面的代码可以自己改,parent 完全可以去掉,root 节点在我这里是没有父节点的 node,你可以改成一个 list,或者把 root id 当参数传进去。lambda stream 和泛型,理解原理很简单。
wuzhizuiguo
2019-12-23 18:46:52 +08:00
@aguesuka 是的,可以改一点. 在你的基础上改了下,去掉了 parent. 传了个 parentId 和 ID 的判断相等的 Function. 对泛型类的比较该怎么实现不懂(怎么取参数,怎么判断大小) , 还有就是 Node::new 是什么意思, 调用 Node 的默认构造方法吗? 还是非默认的 Node(E data)?
下面这个是自己改了老半天的, 可能还是有错误, 请路过的谨慎使用... (compare 是判断是否和 pid 的值相等)


public static <E,ID> List<Node<E>> buildTree(Collection<E> elements, Function<E,ID> getID, Function<E,ID> getParentID,
ID pid, Function<E,Boolean> compare){
Map<ID,Node<E>> idNodeMap = elements.stream().collect(Collectors.toMap(getID,Node::new));
//传进来的集合可能没有包含父类 /最顶级父类
Node<E> root = null;
//返回的列表
List<Node<E>> nodes = new ArrayList<>();
for (E element : elements){
ID selfId = getID.apply(element);
ID parentId = getParentID.apply(element);
Node<E> selfNode = idNodeMap.get(selfId);
//如果这个集合里 有元素有对应的父节点
if (idNodeMap.containsKey(parentId)){
Node<E> parentNode = idNodeMap.get(parentId);
// selfNode.setParent(parentNode); //取消设置父节点,不然看起来很庞大
parentNode.getChildren().add(selfNode);
//如果设置了父节点 pid,而且该节点的父节点也是设置的值 pid
if (pid!=null &&compare.apply(element) && !nodes.contains(selfNode)){
nodes.add(selfNode);
}
}else {
root = selfNode;
//如果设置了父节点 pid,而且该节点的父节点也是设置的值 pid
if (pid!=null &&compare.apply(element) && !nodes.contains(selfNode)){
nodes.add(root);
}

}
}
return nodes;
}


public class Node<E> {
E data;
Set<Node<E>> children;
Node<E> parent;

public Node(E data) {
this.data = data;
children = new HashSet<>();
}

public E getData() {
return data;
}

public void setData(E data) {
this.data = data;
}

public Set<Node<E>> getChildren() {
return children;
}

public void setChildren(Set<Node<E>> children) {
this.children = children;
}

public Node<E> getParent() {
return parent;
}

public void setParent(Node<E> parent) {
this.parent = parent;
}
}

具体调用
List<MenuVO> menuVOList =... 从数据库查出来的结果列表
// 去父节点 pid 为 2 的, 获取该菜单下所有的栏目,
Collection<Node<MenuVO>> nodes = buildTree(menuVOList,e->e.getId(),e->e.getPid(),2,e->e.getPid().intValue() ==2 );
aguesuka
2019-12-23 21:13:53 +08:00
@wuzhizuiguo node 的 parent 字段是可以去掉的。内部类 Node 必须是 static 的,要不就单独建一个类。如果你想要 id 为 2 的菜单的所有子菜单,直接把 id 为 2 的菜单返回回来,它的 children 就是。一般情况比较可以用 Objects.equals。Function 可以用 Predicate 代替,::new 不好解释,你可以上网上搜搜。 如果还有别的问题可以加我微信。MTMzNzA4NDMyNjE= ( base64 )
wuzhizuiguo
2019-12-24 09:47:25 +08:00
@aguesuka 谢谢.是的,我把 Node 类放到外面单独建了, 返回 id 为 2 的菜单 用它的 children 这个方法好.谢谢!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/623139

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX