【问题标题】:Convert java arrayList of Parent/child relation into tree?将父/子关系的java arrayList转换为树?
【发布时间】:2015-06-01 09:38:30
【问题描述】:

我有一堆父/子对,我想尽可能将它们变成分层树结构。例如,这些可能是配对:

Child : Parent
    H : Ga
    F : G
    G : D
    E : D
    A : E
    B : C
    C : E
    D : NULL
    Z : Y
    Y : X
    X: NULL

需要转化为(a)层次树:

   D
    ├── E
    │   ├── A
    │   │   └── B
    │   └── C   
    └── G
    |   ├── F
    |   └── H
    |
    X
    |
    └── Y
        |
        └──Z

在 Java 中,我如何从包含 child=>parent 对的 arrayList 转到类似的 Tree?

我需要这个操作的输出是arrayList包含两个元素D和X 反过来,每个人都有其孩子的列表,而孩子的列表又包含一个孩子的列表,依此类推

public class MegaMenuDTO {
    private String Id;
    private String name;
    private String parentId;
    private List<MegaMenuDTO> childrenItems=new ArrayList<MegaMenuDTO>();

    public String getId() {
        return Id;
    }
    public void setId(String id) {
        Id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getParentId() {
        return parentId;
    }
    public void setParentId(String parentId) {
        this.parentId = parentId;
    }
    public List<MegaMenuDTO> getChildrenItems() {
        return childrenItems;
    }
    public void setChildrenItems(List<MegaMenuDTO> childrenItems) {
        this.childrenItems = childrenItems;
    }
}

我的第一次尝试是

private void arrangeMegaMenuTree(MegaMenuDTO grandParent,
        MegaMenuDTO parent, List<MegaMenuDTO> children) {

    for (MegaMenuDTO child : children) {
        if (child.getParentId().equals(parent.getId())) {
            arrangeMegaMenuTree(parent, child, children);
        }
    }

    if (!grandParent.getId().equals(parent.getId())) {
        grandParent.getChildrenItems().add(parent);
        // children.remove(parent);
    }

}

再试一次

private List<MegaMenuDTO> arrangeMegaMenuTree(MegaMenuDTOparent,List<MegaMenuDTO>menuItems) {

    for (MegaMenuDTO child : menuItems) {

        if (parent.getId().equals(child.getId())) {
            continue;
        }
        if (hasChildren(child, menuItems)) {
            parent.setChildrenItems(arrangeMegaMenuTree(child, menuItems
                    .subList(menuItems.indexOf(child), menuItems.size())));
        } else {
            List<MegaMenuDTO> tempList = new ArrayList<MegaMenuDTO>();
            tempList.add(child);
            return tempList;
        }
    }
    return null;
}

private boolean hasChildren(MegaMenuDTO parent, List<MegaMenuDTO> children) {
    for (MegaMenuDTO child : children) {

        if (child.getParentId().equals(parent.getId())) {
            return true;
        }
    }
    return false;
}

【问题讨论】:

  • 请问您的第一次代码尝试是什么?你打算如何在 java 中表示树?​​span>
  • 通过树我的意思是元素有一个元素列表,其中每个元素都包含元素的点亮等等
  • 很抱歉打扰你,但请不要把你所有的getter和setter都告诉你,越精确越好。
  • 解决方案stackoverflow.com/a/30570416/813853 似乎是合法的!

标签: java


【解决方案1】:

假设你的 Node 结构是这样的:

class Node {
  Object id;
  List<Node> children;
  Node parent;

  public Node(Object id) {
    this.id = id;
    children = new LinkedList<>();      
  }
}

然后你首先开始迭代你的输入列表,并从 ids->Nodes 创建一个映射(这用于在树仍然是非结构化的时候获取节点);

Map<Object, Node> temp = new HashMap<>();
for (Pair pair: inputList) {
  Node parent = temp.getOrDefault(pair.parent.id, new Node(pair.parent.id));
  Node child = temp.getOrDefault(pair.child.id, new Node(pair.child.id));
  parent.children.add(child);
  child.parent = parent;
  temp.put(parent.id, parent);
  temp.put(child.id, child);
}

现在您可以在地图上迭代搜索树的根

for (Node n: temp.values()) {
  if (n.parent==null) {
    root = n; break;
  }
}

此代码假定您的数据是“有效的”(没有重复的子条目、单个根等)。否则您可以轻松地对其进行调整。

【讨论】:

  • 既然我们做了很多get 我们最好使用ArrayList 然后LinkedList,检查一下我可能错了stackoverflow.com/questions/322715/…
  • 我刚刚拿到了我能想到的第一个系列。孩子们更有可能是一个集合而不是一个列表。或者,如果您需要链接上的一些额外信息,您可以有一张从 ID 到 Info 的地图。我没有其他信息可以做出更明智的选择(我的代码没有在孩子列表中做一次获取:)
  • 抱歉,我正在查看我的代码...(看到 get 和 comment)
  • 精确解决方案谢谢
【解决方案2】:

这是基于第一个答案和问题更新的替代解决方案...... :)

主要方法

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main2 {

    public static void main(String[] args) {

        // input
        ArrayList<Pair> pairs = new ArrayList<Pair>();
        pairs.add(new Pair( "H" , "G"));
        pairs.add(new Pair( "F" , "G"));
        pairs.add(new Pair( "G" , "D"));
        // ...


        // Arrange
        // String corresponds to the Id
        Map<String, MegaMenuDTO> hm = new HashMap<>();


        // you are using MegaMenuDTO as Linked list with next and before link 

        // populate a Map
        for(Pair p:pairs){

            //  ----- Child -----
            MegaMenuDTO mmdChild ;
            if(hm.containsKey(p.getChildId())){
                mmdChild = hm.get(p.getChildId());
            }
            else{
                mmdChild = new MegaMenuDTO();
                hm.put(p.getChildId(),mmdChild);
            }           
            mmdChild.setId(p.getChildId());
            mmdChild.setParentId(p.getParentId());
            // no need to set ChildrenItems list because the constructor created a new empty list



            // ------ Parent ----
            MegaMenuDTO mmdParent ;
            if(hm.containsKey(p.getParentId())){
                mmdParent = hm.get(p.getParentId());
            }
            else{
                mmdParent = new MegaMenuDTO();
                hm.put(p.getParentId(),mmdParent);
            }
            mmdParent.setId(p.getParentId());
            mmdParent.setParentId("null");                              
            mmdParent.addChildrenItem(mmdChild);


        }

        // Get the root
        List<MegaMenuDTO> DX = new ArrayList<MegaMenuDTO>(); 
        for(MegaMenuDTO mmd : hm.values()){
            if(mmd.getParentId().equals("null"))
                DX.add(mmd);
        }

        // Print 
        for(MegaMenuDTO mmd: DX){
            System.out.println("DX contains "+DX.size()+" that are : "+ mmd);
        }

    }

}

对类:

public class Pair {
    private String childId ;
    private String parentId;

    public Pair(String childId, String parentId) {
        this.childId = childId;
        this.parentId = parentId;
    }
    public String getChildId() {
        return childId;
    }
    public void setChildId(String childId) {
        this.childId = childId;
    }
    public String getParentId() {
        return parentId;
    }
    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

}

MegaMenuDTO 类已更新

import java.util.ArrayList;
import java.util.List;

public class MegaMenuDTO {

    private String Id;
    private String name;
    private String parentId;
    private List<MegaMenuDTO> childrenItems; 

    public MegaMenuDTO() {
        this.Id = "";
        this.name = "";     
        this.parentId = "";
        this.childrenItems = new ArrayList<MegaMenuDTO>();
    }

    public String getId() {
        return Id;
    }
    public void setId(String id) {
        Id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getParentId() {
        return parentId;
    }
    public void setParentId(String parentId) {
        this.parentId = parentId;
    }
    public List<MegaMenuDTO> getChildrenItems() {
        return childrenItems;
    }
    public void setChildrenItems(List<MegaMenuDTO> childrenItems) {
        this.childrenItems = childrenItems;
    }
    public void addChildrenItem(MegaMenuDTO childrenItem){
        if(!this.childrenItems.contains(childrenItem))
            this.childrenItems.add(childrenItem);
    }

    @Override
    public String toString() {
        return "MegaMenuDTO [Id=" + Id + ", name=" + name + ", parentId="
                + parentId + ", childrenItems=" + childrenItems + "]";
    }

}

【讨论】:

  • 只有一个字“完美”
  • 如果您的输入包含根也输入:H : Ga F : G G : D E : D A : E B : C C : E D : D 此解决方案将给您堆栈溢出异常 请注意输入。
  • 我必须先走一步。因为我有一个通用列表,其中每个条目都包含父 ID,所以在进入此解决方案之前我必须先配对。
  • @MeladBasilius 嗨,我怎样才能将它用于 recylerview?你会帮我吗?
【解决方案3】:

试试这个

只需根据您的 id 对其进行哈希处理并建立您的关系。

完成后找到树的根。

    private MegaMenuDTO getRoot(Map<String, MegaMenuDTO> tree) {
        return tree.values().stream().filter(node -> Objects.isNull(node.getParentId())).findFirst().orElse(null);
    }

    public MegaMenuDTO createTree(List<MegaMenuDTO> list) {
        Map<String, MegaMenuDTO> tree = new HashMap<>();
        list.forEach(current -> tree.put(current.getId(), current));
        list.forEach(current -> {
            String parentId = current.getParentId();
            if (tree.containsKey(parentId)) {
                MegaMenuDTO parent = tree.get(parentId);
                current.setParentId(parentId);
                parent.addChildrenItem(current);
                tree.put(parentId, parent);
                tree.put(current.getId(), current);
            }
        });
        return getRoot(tree);
    }

【讨论】:

    【解决方案4】:

    取决于@Diego Martinoia 解决方案和@OSryx 解决方案,我的问题的最终解决方案

    private List<MegaMenuDTO> dooo(List<MegaMenuDTO> input) {
        Map<String, MegaMenuDTO> hm = new HashMap<String, MegaMenuDTO>();
        MegaMenuDTO child = null;
        MegaMenuDTO mmdParent = null;
    
        for (MegaMenuDTO item : input) {
    
            // ------ Process child ----
            if (!hm.containsKey(item.getId())) {
                hm.put(item.getId(), item);
            } 
            child = hm.get(item.getId());
    
            // ------ Process Parent ----
            if (item.getParentId() != null && !item.getParentId().equals("")
                    && !item.getParentId().equals("0")) {
                if (hm.containsKey(item.getParentId())) {
                    mmdParent = hm.get(item.getParentId());
                    mmdParent.getChildrenItems().add(child);
                }
            }
    
        }
    
        List<MegaMenuDTO> DX = new ArrayList<MegaMenuDTO>();
        for (MegaMenuDTO mmd : hm.values()) {
            if (mmd.getParentId() == null || mmd.getParentId().equals("")
                    || mmd.getParentId().equals("0"))
                DX.add(mmd);
        }
        return DX;
    }
    

    【讨论】:

    • 如果您的节点是随机排序的或在列表中自下而上,我认为这不会起作用。您将拥有一堆不会链接到其父节点的孤立节点。例如,要处理的第一个节点在地图中不会有其父节点,因此它将始终是孤立的。
    【解决方案5】:

    这是我的解决方案:

    • 将 MegaMenuDTO 列表转换为地图:

      public static  Map<String, MegaMenuDTO> buildIdMap(Collection<MegaMenuDTO> targets){
          Map<String, MegaMenuDTO> result = new HashMap<>();
          if(targets!=null && !targets.isEmpty()){
              final Iterator<MegaMenuDTO> iterator = targets.iterator();
              while (iterator.hasNext()){
                  final MegaMenuDTO next = iterator.next();
                  if(StringUtils.isNotBlank(next.getId())){
                      result.put(next.getId(),next);
                  }
              }
          }
          return result;
      }
      
    • 然后将地图转换为树:

      public List<MegaMenuDTO> getTree(List<MegaMenuDTO> all ){
          final List<MegaMenuDTO> result = new ArrayList<>();
          final Map<String, MegaMenuDTO> allMap = buildIdMap(all);
          final Iterator<MegaMenuDTO> iterator = all.iterator();
          while (iterator.hasNext()){
              final MegaMenuDTO next = iterator.next();
              final String parentId = next.getParentId();
              if(StringUtils.isNotBlank(parentId)){
                  final MegaMenuDTO node = allMap.get(next.getId());
                  final MegaMenuDTO nodeP = allMap.get(parentId);
                  if(nodeP != null){
                      nodeP.getChildren().add(node);
                  }
              }else{
                  next.setOpen(true);
                  result.add(next);
              }
          }
          return result;
      }
      

    【讨论】:

      猜你喜欢
      • 2012-06-01
      • 2015-08-15
      • 2011-02-24
      • 1970-01-01
      • 2019-01-11
      • 2012-08-07
      • 1970-01-01
      相关资源
      最近更新 更多