我有一个从postgres数据库中提取数据的家庭类。这个类看起来像这样:
@Entity
public class Family(){
@Id
private long id;
private String firstName;
private String lastName;
private Long parentId;
private4 String familyPath;
private List<Family> children;
//getters and setters
在数据库中,我将彼此的关系存储为以句点分隔的字符串。因此,例如,如果Bob是Sue的子节点,则树列将如下所示:“bob.sue”。此路径存储为familyPath变量中族对象的一部分。
澄清 familyPath是基于数据库中每行的唯一ID的路径。所以路径可能看起来像“1.2.3”,其中最后一个数字是当前行。 “1.2.4”是另一条潜在的道路。所以ID为3和4的行是2的子等,
在我的代码中,我在数据库中查询数据中所有系列成员,因此我在数据库中拥有该系列的每个成员。我的目标是使用这个初始的平面集生成一组所有家庭成员作为层次结构。所以,最后如果我在Bob上调用getChildren,我会得到一个包含Sue和其他孩子的列表。
我的解决方案:
首先,我遍历我的家庭列表,找到我称之为根成员 - 家庭路径顶层的成员 - 并将它们删除到一个单独的列表中。所以现在我有一个顶级家庭成员列表,一个其他人的列表。
然后,对于顶级列表中的每个成员,我调用以下递归方法:
private Family familyTree(Family root, List<Family> members) {
List<Family> children = new ArrayList<>();
for (Family f : members) {
if (isChildOf(f, root)) {
children.add(familyTree(f, resources));
}
}
root.setChildren(children);
return root;
}
private boolean isChildOf(Family a, Family b) {
String pCPath = a.getFamilyPath();
String pPPath = b.getFamilyPath();
return pCPath.indexOf('.') >= 0
&& pCPath.substring(0, pCPath.lastIndexOf('.')).equals(pPPath);
}
并将输出保存到列表中。这会产生预期的结果。
我的问题 但是,我觉得这种递归方法非常昂贵(n ^ 2)。我认为可能有一种更有效的方法来使用集合,映射和Family对象的familyPath变量生成此层次结构,但我仍然陷入多个迭代循环中。有人有想法吗?
答案 0 :(得分:0)
private Family familyTree(Family root, List<Family> members) {
Map<Long, List<Family>> parentMap = new HashMap<>();
// Assuming root is not contained in members
root.children = new ArrayList<>();
parentMap.put(root.id, root.children);
// Assign each member to a child list
for (Family member : members) {
// Put the family member in the right child list
Long parentId = member.getParentId();
List<Family> parentChildren = parentMap.get(parentId);
if (parentChildren == null) {
parentChildren = new ArrayList<>();
parentMap.put(parentId, parentChildren);
}
parentChildren.add(member);
// Get or create the child list of the family member
List<Family> ownChildren = parentMap.get(member.id);
if (ownChildren == null) {
ownChildren = new ArrayList<>();
parentMap.put(member.id, ownChildren);
}
member.children = ownChildren;
}
return root;
}
private Long getParentId() {
// left as an exercise...
}
private List<Family> familyTree(List<Family> members) {
List<Family> roots = new ArrayList<>();
Map<Long, List<Family>> parentMap = new HashMap<>();
// Assign each member to a child list
for (Family member : members) {
// Put the family member in the right child list
Long parentId = member.getParentId();
if (parentId == null) {
// a root member
roots.add(member);
} else {
// a non-root member
List<Family> parentChildren = parentMap.get(parentId);
if (parentChildren == null) {
parentChildren = new ArrayList<>();
parentMap.put(parentId, parentChildren);
}
parentChildren.add(member);
}
// Get or create the child list of the family member
List<Family> ownChildren = parentMap.get(member.id);
if (ownChildren == null) {
ownChildren = new ArrayList<>();
parentMap.put(member.id, ownChildren);
}
member.children = ownChildren;
}
return roots;
}
您的Family
课程应具有private Family parent
属性。然后,您将能够按系列“级别”执行单个查询。那就是:
可以修改数据库模式以在单个查询中检索整个子树。诀窍是给每个树节点一个“左”和“右”值。这些值为节点的子节点的“左”和“右”值建立范围。
然后可以像这样选择完整的树:
SELECT child.id, ...
FROM family AS child, family AS parent
WHERE child.lft BETWEEN parent.lft AND parent.rgt
AND parent.id = 111
ORDER BY child.lft;
还有许多其他分层操作可以使用这种模式轻松完成。有关详细信息,请参阅this post和 Joe Celko在SQL中的树和层次结构中的Smarties 。
最后,您的模型只考虑每个家庭成员的单亲,这看起来很奇怪。