想用java实现一个无限级树型菜单,数据库是这样设计的:id,pid(父id),name.用递归调用,要一次性加载.
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了想用java实现一个无限级树型菜单,数据库是这样设计的:id,pid(父id),name.用递归调用,要一次性加载.相关的知识,希望对你有一定的参考价值。
无限级是指树可能是无限深度的么?如果是的话,用java恐怕没法子一次性加载,得用sql语句一次把数据查出来。追问是指无限深度,就是用sql语句一次把数据查出来
追答sql非我所长……
参考技术A 如果是hibernate我知道怎么搞。构造无限级树的框架套路,附上python/golang/php/js实现
前言
框架思维非常重要,和语言无关,这是一种非常重要的抽象能力,吹得厉害一点,就是要有高屋建瓴,统筹全局的能力。
无限级树型结构的创建也是有套路的,下面由伟大的诗人chenqionghe给出套路框架,再分别套上相应的编程语言实现。
最终达到看到框架就能实现对应编程语言的代码,或者也可以直接copy实现拿去用~
迭代实现参考了这篇文章的思路列表list转树形结构:(python/golang/js/php),童鞋们可以看看这篇文章
需求
给定一级列表,构造出指定父ID的无限级树型结果
数据
[
{\'id\': 1, \'parent_id\': 0, \'name\': "A"},
{\'id\': 2, \'parent_id\': 1, \'name\': "AA"},
{\'id\': 3, \'parent_id\': 1, \'name\': "AB"},
{\'id\': 4, \'parent_id\': 3, \'name\': "ABA"},
{\'id\': 5, \'parent_id\': 3, \'name\': "ABB"},
{\'id\': 6, \'parent_id\': 3, \'name\': "ABC"},
{\'id\': 7, \'parent_id\': 1, \'name\': "AC"},
{\'id\': 8, \'parent_id\': 7, \'name\': "ACA"},
{\'id\': 9, \'parent_id\': 8, \'name\': "ACAA"},
{\'id\': 10, \'parent_id\': 8, \'name\': "ACAB"},
]
结果
最后构造成一棵根据父ID连接起来的树结构
{
"id": 1,
"parent_id": 0,
"name": "A",
"children": [
{
"id": 2,
"parent_id": 1,
"name": "AA",
"children": []
},
{
"id": 3,
"parent_id": 1,
"name": "AB",
"children": [
{
"id": 4,
"parent_id": 3,
"name": "ABA",
"children": []
},
{
"id": 5,
"parent_id": 3,
"name": "ABB",
"children": []
},
{
"id": 6,
"parent_id": 3,
"name": "ABC",
"children": []
}
]
},
{
"id": 7,
"parent_id": 1,
"name": "AC",
"children": [
{
"id": 8,
"parent_id": 7,
"name": "ACA",
"children": [
{
"id": 9,
"parent_id": 8,
"name": "ACAA",
"children": []
},
{
"id": 10,
"parent_id": 8,
"name": "ACAB",
"children": []
}
]
}
]
}
]
}
框架
递归框架
递归是一门艺术,用接近人类的语言来表达了程序,优点是代码较少,缺点是性能较差。
获取树(列表,父ID)
res = []
for 节点 in 列表:
if 节点的parent_id 等于 父ID
节点.children = 获取树(列表, 节点ID)
res.add(节点)
return res
迭代框架
迭代本身是创建一条引用链,将所有的节点串起来
获取树(列表,父ID)
memo = {}
for 节点 in 列表:
//构造memo给节点的父ID查找追加节点用
if 节点ID in memo:
节点.children = memo[节点ID].children //之前构造的children数组覆盖当前节点的children
memo[节点ID] = 节点
else
节点.children = []
memo[节点ID] = 节点
//给像父对象的children追加
if 节点父ID in memo:
memo[节点父ID].children.add(memo[节点ID]) //追加当前构造的ID节点
else:
memo[节点父ID] = {\'children\':[memo[节点ID]]} //初始化父对象再追加
return memo[父ID].children
递归框架实现
python
def get_tree_iterative(list_data, parent_id=0):
memo = {}
for v in list_data:
item_id = v[\'id\']
item_paren_id = v[\'parent_id\']
if item_id in memo:
v[\'children\'] = memo[item_id][\'children\']
memo[item_id] = v
else:
v[\'children\'] = []
memo[item_id] = v
if item_paren_id in memo:
memo[item_paren_id][\'children\'].append(memo[item_id])
else:
memo[item_paren_id] = {\'children\': memo[item_id]}
return memo[parent_id][\'children\']
list_data = [
{\'id\': 1, \'parent_id\': 0, \'name\': "A"},
{\'id\': 2, \'parent_id\': 1, \'name\': "AA"},
{\'id\': 3, \'parent_id\': 1, \'name\': "AB"},
{\'id\': 4, \'parent_id\': 3, \'name\': "ABA"},
{\'id\': 5, \'parent_id\': 3, \'name\': "ABB"},
{\'id\': 6, \'parent_id\': 3, \'name\': "ABC"},
{\'id\': 7, \'parent_id\': 1, \'name\': "AC"},
{\'id\': 8, \'parent_id\': 7, \'name\': "ACA"},
{\'id\': 9, \'parent_id\': 8, \'name\': "ACAA"},
{\'id\': 10, \'parent_id\': 8, \'name\': "ACAB"},
]
res = get_tree_iterative(list_data)
import json
print(json.dumps(res, indent=4))
运行如下
golang
package main
import (
"encoding/json"
"fmt"
)
type Node struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
Children []*Node `json:"children"`
}
func getTreeRecursive(list []*Node, parentId int) []*Node {
res := make([]*Node, 0)
for _, v := range list {
if v.ParentId == parentId {
v.Children = getTreeRecursive(list, v.Id)
res = append(res, v)
}
}
return res
}
func main() {
list := []*Node{
{4, 3, "ABA", nil},
{3, 1, "AB", nil},
{1, 0, "A", nil},
{2, 1, "AA", nil},
{5, 3, "ABB", nil},
{6, 3, "ABC", nil},
{7, 1, "AC", nil},
{8, 7, "ACA", nil},
{9, 8, "ACAA", nil},
{10, 8, "ACAB", nil},
}
res := getTreeRecursive(list, 0)
bytes, _ := json.MarshalIndent(res, "", " ")
fmt.Printf("%s\\n", bytes)
}
运行如下
php
function getTreeRecursive(&$list, $parentId = 0)
{
$res = [];
foreach ($list as $k => $v) {
if ($v[\'parent_id\'] == $parentId) {
$v[\'children\'] = getTreeRecursive($list, $v[\'id\']);
$res[] = $v;
}
}
return $res;
}
$list = [
[\'id\' => 4, \'parent_id\' => 3, \'name\' => "ABA"],
[\'id\' => 3, \'parent_id\' => 1, \'name\' => "AB"],
[\'id\' => 1, \'parent_id\' => 0, \'name\' => "A"],
[\'id\' => 2, \'parent_id\' => 1, \'name\' => "AA"],
[\'id\' => 5, \'parent_id\' => 3, \'name\' => "ABB"],
[\'id\' => 6, \'parent_id\' => 3, \'name\' => "ABC"],
[\'id\' => 7, \'parent_id\' => 1, \'name\' => "AC"],
[\'id\' => 8, \'parent_id\' => 7, \'name\' => "ACA"],
[\'id\' => 9, \'parent_id\' => 8, \'name\' => "ACAA"],
[\'id\' => 10, \'parent_id\' => 8, \'name\' => "ACAB"],
];
$res = getTreeRecursive($list);
echo json_encode($res, JSON_PRETTY_PRINT);
运行结果如下
js
function getTreeRecursive(listData, parentId = 0) {
let res = []
listData.forEach((v, k) => {
if (v.parent_id == parentId) {
v.children = getTreeRecursive(listData, v.id)
res.push(v)
}
})
return res
}
dataList = [
{\'id\': 1, \'parent_id\': 0, \'name\': "A"},
{\'id\': 2, \'parent_id\': 1, \'name\': "AA"},
{\'id\': 3, \'parent_id\': 1, \'name\': "AB"},
{\'id\': 4, \'parent_id\': 3, \'name\': "ABA"},
{\'id\': 5, \'parent_id\': 3, \'name\': "ABB"},
{\'id\': 6, \'parent_id\': 3, \'name\': "ABC"},
{\'id\': 7, \'parent_id\': 1, \'name\': "AC"},
{\'id\': 8, \'parent_id\': 7, \'name\': "ACA"},
{\'id\': 9, \'parent_id\': 8, \'name\': "ACAA"},
{\'id\': 10, \'parent_id\': 8, \'name\': "ACAB"},
]
let res = getTreeRecursive(dataList);
console.log((JSON.stringify(res, null, 4)))
运行如下
迭代框架实现
python
def get_tree_iterative(list_data, parent_id=0):
memo = {}
for v in list_data:
id_ = v[\'id\']
item_paren_id = v[\'parent_id\']
if id_ in memo:
v[\'children\'] = memo[id_][\'children\']
memo[id_] = v
else:
v[\'children\'] = []
memo[id_] = v
if item_paren_id in memo:
memo[item_paren_id][\'children\'].append(memo[id_])
else:
memo[item_paren_id] = {\'children\': memo[id_]}
return memo[parent_id][\'children\']
list_data = [
{\'id\': 1, \'parent_id\': 0, \'name\': "A"},
{\'id\': 2, \'parent_id\': 1, \'name\': "AA"},
{\'id\': 3, \'parent_id\': 1, \'name\': "AB"},
{\'id\': 4, \'parent_id\': 3, \'name\': "ABA"},
{\'id\': 5, \'parent_id\': 3, \'name\': "ABB"},
{\'id\': 6, \'parent_id\': 3, \'name\': "ABC"},
{\'id\': 7, \'parent_id\': 1, \'name\': "AC"},
{\'id\': 8, \'parent_id\': 7, \'name\': "ACA"},
{\'id\': 9, \'parent_id\': 8, \'name\': "ACAA"},
{\'id\': 10, \'parent_id\': 8, \'name\': "ACAB"},
]
res = get_tree_iterative(list_data)
import json
print(json.dumps(res, indent=4))
运行如下
golang
package main
import (
"encoding/json"
"fmt"
)
type Node struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
Children []*Node `json:"children"`
}
func getTreeIterative(list []*Node, parentId int) []*Node {
memo := make(map[int]*Node)
for _, v := range list {
if _, ok := memo[v.Id]; ok {
v.Children = memo[v.Id].Children
memo[v.Id] = v
} else {
v.Children = make([]*Node, 0)
memo[v.Id] = v
}
if _, ok := memo[v.ParentId]; ok {
memo[v.ParentId].Children = append(memo[v.ParentId].Children, memo[v.Id])
} else {
memo[v.ParentId] = &Node{Children: []*Node{memo[v.Id]}}
}
}
return memo[parentId].Children
}
func main() {
list := []*Node{
{4, 3, "ABA", nil},
{3, 1, "AB", nil},
{1, 0, "A", nil},
{2, 1, "AA", nil},
{5, 3, "ABB", nil},
{6, 3, "ABC", nil},
{7, 1, "AC", nil},
{8, 7, "ACA", nil},
{9, 8, "ACAA", nil},
{10, 8, "ACAB", nil},
}
res := getTreeIterative(list, 0)
bytes, _ := json.MarshalIndent(res, "", " ")
fmt.Printf("%s\\n", bytes)
}
运行如下
php
function getTreeIterative($list, $parentId = 0)
{
$memo = [];
foreach ($list as &$v) {
$id = $v[\'id\'];
$itemParentId = $v[\'parent_id\'];
if (isset($memo[$id])) {
$v[\'children\'] = &$memo[$id][\'children\'];
$memo[$id] = $v;
} else {
$v[\'children\'] = [];
$memo[$id] = $v;
}
if (isset($memo[$itemParentId])) {
$memo[$itemParentId][\'children\'][] = &$memo[$id];
} else {
$memo[$itemParentId] = [\'children\' => [&$memo[$id]]];
}
}
return $memo[$parentId][\'children\'];
}
$list = [
[\'id\' => 4, \'parent_id\' => 3, \'name\' => "ABA"],
[\'id\' => 3, \'parent_id\' => 1, \'name\' => "AB"],
[\'id\' => 1, \'parent_id\' => 0, \'name\' => "A"],
[\'id\' => 2, \'parent_id\' => 1, \'name\' => "AA"],
[\'id\' => 5, \'parent_id\' => 3, \'name\' => "ABB"],
[\'id\' => 6, \'parent_id\' => 3, \'name\' => "ABC"],
[\'id\' => 7, \'parent_id\' => 1, \'name\' => "AC"],
[\'id\' => 8, \'parent_id\' => 7, \'name\' => "ACA"],
[\'id\' => 9, \'parent_id\' => 8, \'name\' => "ACAA"],
[\'id\' => 10, \'parent_id\' => 8, \'name\' => "ACAB"],
];
$res = getTreeIterative($list);
echo json_encode($res, JSON_PRETTY_PRINT);
运行结果如下
js
function getTreeIterative(listData, parentId = 0) {
let memo = {};
listData.forEach((v, k) => {
let id = v.id
let itemParentId = v.parent_id
if (memo[id]) {
v.children = memo[id].children
memo[id] = v
} else {
v.children = []
memo[id] = v;
}
if (memo[itemParentId]) {
memo[itemParentId].children.push(memo[id]);
} else {
memo[itemParentId] = {children: [memo[id]]};
}
})
return memo[parentId].children
}
dataList = [
{\'id\': 1, \'parent_id\': 0, \'name\': "A"},
{\'id\': 2, \'parent_id\': 1, \'name\': "AA"},
{\'id\': 3, \'parent_id\': 1, \'name\': "AB"},
{\'id\': 4, \'parent_id\': 3, \'name\': "ABA"},
{\'id\': 5, \'parent_id\': 3, \'name\': "ABB"},
{\'id\': 6, \'parent_id\': 3, \'name\': "ABC"},
{\'id\': 7, \'parent_id\': 1, \'name\': "AC"},
{\'id\': 8, \'parent_id\': 7, \'name\': "ACA"},
{\'id\': 9, \'parent_id\': 8, \'name\': "ACAA"},
{\'id\': 10, \'parent_id\': 8, \'name\': "ACAB"},
]
let res = getTreeIterative(dataList);
console.log((JSON.stringify(res, null, 4)))
运行如下
以后如果想用直接来拷贝代码就行,就是这么简单~
以上是关于想用java实现一个无限级树型菜单,数据库是这样设计的:id,pid(父id),name.用递归调用,要一次性加载.的主要内容,如果未能解决你的问题,请参考以下文章