2014-11-26 1 views
3

저는 Java 8 Streams API에 상당히 익숙하지만 새로운 기능을 위해 사용하기로 결정했지만 벽돌 벽에 부딪혔습니다!스트림 트리 변환

MenuItem parent1 = new MenuItem(0L, "Code Parent", "Description Parent"); 
MenuItem item1 = new MenuItem(1L, "Code1", "Description1"); 
MenuItem item2 = new MenuItem(2L, "Code2", "Description2"); 
MenuItem item3 = new MenuItem(3L, "Code3", "Description3"); 
MenuItem item4 = new MenuItem(4L, "Code4", "Description4"); 

나는 또한 MenuItem의 (상위/하위) 사이의 계층 적 관계를 표현 MenuHierarchy 개체의 무리가 있습니다

나는의 MenuItem 개체의 무리가 있습니다. 이 모델은있는 그대로 수정되었으므로 내가 가지고있는 것과 함께 작업해야합니다.

생성자는 - MenyHierarchy (ID, 부모, 자식, displayOrder)

MenuHierarchy hierarchy1 = new MenuHierarchy(1L, null, parent1); 
MenuHierarchy hierarchy2 = new MenuHierarchy(2L, parent1, item1, 1); 
MenuHierarchy hierarchy3 = new MenuHierarchy(3L, item1, item2, 2); 
MenuHierarchy hierarchy4 = new MenuHierarchy(4L, item2, item3, 3); 
MenuHierarchy hierarchy5 = new MenuHierarchy(5L, item3, item4, 4); 

MenuHierarchy 객체는 null의 부모는 루트 노드 간주됩니다.

public class MenuNode implements GenericNode<MenuItem> { 

    private MenuItem data; 
    private List<GenericNode<MenuItem>> children; 

    public MenuNode(MenuItem data) { 
     this.data = data; 
     this.children = new ArrayList<GenericNode<MenuItem>>(); 
    } 

    // Getters, setters 

} 

은 내가 지금까지 무엇을 설명 할 것이다 :

이제 내가 생성 한 MenuNode 엔티티를 사용하여 구조 같은 트리에이 관계를 변환 할 스트림 API를 사용하여

 /* This is the list of Root Menus (Menus which have no parent) */ 
    List<MenuNode> rootNodes = new ArrayList<>(); 
    List<MenuHierarchy> hierarchyList = Arrays.asList(hierarchy1, hierarchy2, hierarchy3, hierarchy4, hierarchy5); 

    /* This first stream adds a new root MenuNode object to the above list ordered by the hierarchy display order */ 
    hierarchyList.parallelStream() 
     .filter((h) -> Objects.isNull(h.getParentMenu())) 
     .sorted((h, i) -> h.getDisplayOrder().compareTo(i.getDisplayOrder())) 
     .map(MenuHierarchy::getChildMenu) 
     .forEachOrdered((i) -> rootNodes.add(new MenuNode(i))); 

    /* This second one is where i've sort of failed... 
     What i need this to do is iterate over the menu hierarchies and for each non-root one 
     add it to the MenuNode children collection where MenuNode.data == MenyHeirarchy.parentMenu 
     Resulting in a tree of MenuItems... 
    */ 
    hierarchyList.stream() 
     .filter((h) -> Objects.nonNull(h.getParentMenu())) 
     .sorted((h, i) -> h.getDisplayOrder().compareTo(i.getDisplayOrder())) 
     .forEachOrdered((h) -> { 

      rootNodes.stream() 
       .filter((n) -> n.getData().equals(h.getParentMenu())) 
       .forEach((n) -> { 
        n.getChildren().add(new MenuNode(h.getChildMenu())); 
       }); 

     }); 

enter image description here

이 전날 인 경우 당신은 모든 계층을 대표하지 않는 한이 잘 모르겠어요 ... 순간에 제대로 작동하지 않습니다 볼 수 있듯이 스트림으로 가능한가요?

모든 아이디어를 적극 권장합니다.

답변

2

메뉴를 나타내는 MenuNode 개체가 여러 개 나오길 바란다고 올바르게 이해하고 있습니까? 그렇다면 모든 노드 객체를 미리 만들어 내고 자식 목록을 채우는 것이 더 쉬울 것이라고 생각합니다.

// first we make all the nodes and map them to ID 
Map<Long, MenuNode> nodes = hierarchies.stream() 
    .map(MenuHierarchy::getChildMenu) 
    .collect(toMap(MenuItem::getId, MenuNode::new)); 

// and now we go over all hierarchies and add children to appropriate node 
hierarchies.stream() 
    .filter(h -> h.getParent() != null) 
    .sorted(comparing(MenuHierarchy::getDisplayOrder)) 
    .forEach(h -> { 
     long parentId = h.getParentMenu().getId(); 
     long childId = h.getChildMenu().getId(); 
     nodes.get(parentId).getChildren().add(nodes.get(childId)) 
    }); 

또는 두 번째 부분은 먼저 노드를 건너 뛸 수 있습니다. 이 방법을 사용하는 장점은 MenuNode에있는 자식 목록을 변경하지 못하게 할 수 있다는 것입니다. , 완전성을 위해, 당신은 그룹까지 계층 구조를 할 수

nodes.values().forEach(node -> 
    node.setChildren(
     hierarchies.stream() 
      .filter(h -> h.getParentMenu().getId() == node.getData().getId()) 
      .sorted(comparing(MenuHierarchy::getDisplayOrder)) 
      .map(MenuHierarchy::getChildMenu) 
      .map(MenuItem::getId) 
      .map(nodes::get) 
      .collect(toList()) 
    ) 
); 

: 그리고 단점은 반복적으로 (그것이 어떤 현실적인 메뉴의 크기는 중요하지한다하더라도) 입맛에 맞지 않는 모든 계층에 걸쳐 반복의 아이디어를 찾을 수 있다는 것입니다 같은 부모를 위해 다음과 같이 두 번째 부분을 스트림을 사용하여 재 작성 :

hierarchies.stream() 
    .filter(h -> null != h.getParent()) 
    .collect(
     groupingBy(h->g.getParentMenu().getId(), toList()) 
    ) // now we have a map of parent Ids to list of MenuHierarchy for that parent 
    .forEach((parentId, children) -> 
     nodes.get(parentId)).setChildren(
      children.stream() 
       .sorted(comparing(MenuHierarchy::getDisplayOrder)) 
       .map(h -> nodes.get(h.getChildMenu().getId())) 
       .collect(toList()) 
     ) 
    ); 

당신은 당신에게 명확하게 무엇을 결정합니다.

편집 : 계층 구조 ID가 항상 자식 ID와 같은지 확실하지 않았습니다. 그렇다면 코드를 약간 단순화 할 수 있습니다.

+0

안녕하세요 미샤, 답변 해 주셔서 감사합니다. 그 결과 구조가리스트 이 필요합니다. MenuTree는 트리입니다. 내가 가지고있는 문제는 children() 목록에 메뉴 항목을 추가하기 위해 MenuTree 객체를 찾기 위해 재귀를 수행하는 것입니다 ... – Nick

+0

내 의견에있는 해결책은 한 단계 깊숙이 간다 ... 그래서 추가하지 않습니다. 1 단계보다 깊은 어린이 목록에 항목을 추가하십시오. 재귀가 필요한 곳입니다. – Nick