我对Java 8 Streams API的新东西相当新,但是我已经决定将它用于新的功能但是已经碰壁了!
我有一堆MenuItem对象:
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");
我还有一堆MenuHierarchy对象,它们代表MenuItem(父/子)之间的层次关系。这个模型是固定的,所以必须使用我所拥有的。
构造函数 - MenyHierarchy(id,parent,child,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父对象视为根节点。
现在使用流API我想使用我创建的MenuNode实体将此关系转换为树状结构:
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
}
我将解释到目前为止我所拥有的:
/* 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()));
});
});
正如您所看到的那样,目前这种情况并不正常,因为它并不代表所有的层次结构......我不确定流是否可以实现这一点?
将极力推荐任何想法。
答案 0 :(得分:3)
我是否理解你想要最终得到一堆代表菜单的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相同。如果是,则可以简化代码。