使用地图和路径变量

时间:2016-05-10 14:01:11

标签: java database recursion hashmap hierarchy

我有一个从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变量生成此层次结构,但我仍然陷入多个迭代循环中。有人有想法吗?

1 个答案:

答案 0 :(得分:0)

选项1 - 单次传递

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...
}

选项1.b - 单遍遍历所有成员,包括根

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;
}

选项2 - 添加对父

的引用

您的Family课程应具有private Family parent属性。然后,您将能够按系列“级别”执行单个查询。那就是:

  1. 得到苏的所有孩子
  2. 从(1)获取所有人的子女,并将他们分配给适当的父母
  3. 选项3 - 层次结构的嵌套集模型

    可以修改数据库模式以在单个查询中检索整个子树。诀窍是给每个树节点一个“左”和“右”值。这些值为节点的子节点的“左”和“右”值建立范围。

    然后可以像这样选择完整的树:

    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

    最后,您的模型只考虑每个家庭成员的单亲,这看起来很奇怪。