stream构建树形菜单

树形菜单在数据库中存放的方式一般是平铺,通过父级id的方式关联的,
查询出来一般也是直接查询出来,结果是一个平铺的list集合。
后端要处理的话一般使用递归的方式遍历查询。
如果后端不处理的话,也可以直接给前端处理,但是最好还是后端处理好了直接给前端。
这里第一种方式演示递归方式创建树形菜单;
这里第二种方式演示递归+stream方式创建树形菜单;

  1. 先创建Tree节点
@Data
public class Tree {

    private int id; //主键

    private String name;   //节点名字

    private int parentId;   //父节点编号

    private List<Tree> childList;

    public Tree(int id, String name, int parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }
}
  1. 再编写获取子节点的方法
/**
 2. parentNode: 父节点
 3. nodes: 所有的节点集合
 */
private static void  findChildNode(Tree parentNode, List<Tree> nodes) {
    for (Tree node : nodes) {
    	//遍历所有的节点,判断其父节点的id是否和传入的节点id相同
        if (node.getParentId() == parentNode.getId()) {
            List<Tree> childNodes = parentNode.getChild();
            if (childNodes == null) {   //判断该节点的子节点列表是否为空
                childNodes = new ArrayList<>();
            }
            childNodes.add(node);
            parentNode.childList(childNodes);
            //递归查询子节点
            findChildNode(node, nodes); 
        }
    }
}
  1. 获取树形菜单
private static Tree getTree(List<Tree> nodes) {
	//设置根节点
    Tree start = null;
    for (Tree node : nodes) {
        int pid = node.getParentId();
        //默认根节点的pid为0
        if (pid == 0) {
            //根节点
            start = node;
            //查询子节点
            findChildNode(start, nodes);    
        }
    }
    return start;
}
  1. 测试
public static void main (String[] args){
    Tree start = new Tree(1, "根节点", 0);
    Tree node1 = new Tree(2, "A节点", 1);
    Tree node2 = new Tree(3, "B节点", );
    Tree node3 = new Tree(4, "C节点", );
    Tree node4 = new Tree(5, "D节点", 1);
    Tree node5 = new Tree(6, "AA节点", 2);
    Tree node6 = new Tree(7, "AB节点", 2);
    Tree node7 = new Tree(8, "AC节点", 2);
    Tree node8 = new Tree(9, "BA节点", 3);
    Tree node9 = new Tree(10, "BB节点", 3);
    Tree node10 = new Tree(11, "BC节点", 3,);
    List<Tree> nodes = new ArrayList<>();
    nodes.add(start);
    nodes.add(node1);
    nodes.add(node2);
    nodes.add(node3);
    nodes.add(node4);
    nodes.add(node5);
    nodes.add(node6);
    nodes.add(node7);
    nodes.add(node8);
    nodes.add(node9);
    nodes.add(node10);
    Tree tree = getTree(nodes);
    System.out.println(tree);
}

第二种方式: 传统的for循环遍历遍历没有问题,但是我们可以尝试使用递归+stream方式优化我们的代码;
树形节点不变,我们将获取子节点的方式变成递归加stream

private static List<Node> findChildNode(Node parentNode, List<Node> nodes) {
    List<Node> childList = nodes.stream()
    		//过滤出parentNode的子节点
    		.filter(item -> Objects.equals(parentNode.getId(), item.getParentId()))
    		//重新映射成一个新的node节点
            .map(item -> {
            	//这里设置当前节点的子节点列表, 子节点列表的获取是通过递归的方式获取的
                item.setChildList(findChildNode(item, nodes));
                return item;
            }).collect(Collectors.toList());
    return childList;
}

测试

public static void main (String[] args){
    List<Node> nodes = Arrays.asList(
        new Node(1, "根节点", 0),
        new Node(2, "根节点1", 1),
        new Node(3, "根节点1.1", 2),
        new Node(4, "根节点1.2", 2),
        new Node(5, "根节点1.3", 2),
        new Node(6, "根节点2", 1),
        new Node(7, "根节点2.1", 6),
        new Node(8, "根节点2.2", 6),
        new Node(9, "根节点2.2.1", 7),
        new Node(10, "根节点2.2.2", 7),
        new Node(11, "根节点3", 1),
        new Node(12, "根节点3.1", 11));
        
        List<Node> tree = nodes.stream()
        	//过滤出parentId为0的节点为根节点 (如果有多个根节点也可以)
        	.filter(item -> item.getParentId() == 0)
        	.map(item -> {
    			item.setChildList(findChildNode(item, nodes));
    			return item;})
    		.collect(Collectors.toList());
    	Node root = treeList.get(0);
		System.out.println(root);
}

总结
这样做的好处是代码更简洁了,而且节省了中间变量,但是这样对于基础弱的人或者不理解这样子递归的人比较有难度,但是就是因为这样,就一定要多写这样的代码,可以加深stream和递归的认识;
第二种方式不理解的,我简单说下思路:
其实可以理解为从最后一层子节点开始遍历,最后一层子节点肯定没有自己的子节点的,调用findChildNode方法肯定返回null,该子节点就获取完毕,继续遍历同级节点,当遍历结束后跳出方法时,返回的是最后一层子节点的集合,再设置到它们的父节点中,再遍历和父节点同级的节点,一层一层遍历,最后返回的就是整棵树。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Java8 Stream是一种流式计算的方式,可以遍历和操作集合中的元素。遍历可以使用树形结构,也可以使用其他数据结构。树形结构是一种常见的数据结构,可以用于存储和操作具有分层结构的数据。它采用树形而不是线性结构,可以很好地表达具有层次性质的数据。在Java8 Stream中,可以使用树形结构来实现数据的遍历和处理,可以提高代码的效率和可读性。 ### 回答2: Java 8 Stream是一个强大的API工具,可以优雅地遍历树形结构。结构是一种基本的数据结构,它由多个节点组成,每个节点可以有多个子节点。在遍历树形结构时,我们需要访问的每个节点,并对它们进行相应的操作。 Java 8 Stream提供了一套流式编程API,可以更加简洁和高效地操作集合和数组,也可以用来遍历树形结构。下面我们以一个树形结构为例来介绍如何使用Stream遍历树形结构。 首先,我们需要定义一个节点的类,例如: ``` public class TreeNode { private String name; // 节点名字 private List<TreeNode> children; // 子节点 public TreeNode(String name) { this.name = name; this.children = new ArrayList<>(); } public String getName() { return name; } public List<TreeNode> getChildren() { return children; } public void addChild(TreeNode child) { children.add(child); } } ``` 然后,我们可以构建一颗: ``` TreeNode root = new TreeNode("A"); TreeNode b = new TreeNode("B"); TreeNode c = new TreeNode("C"); TreeNode d = new TreeNode("D"); root.addChild(b); root.addChild(c); c.addChild(d); ``` 以上代码建立了一个这样的:A -> B, C -> D。 接下来,我们使用Stream API来遍历这颗: ``` public void visit(TreeNode node) { System.out.println(node.getName()); node.getChildren().stream().forEach(this::visit); } visit(root); ``` 以上代码会输出以下内容: ``` A B C D ``` 我们使用了visit方法来遍历,首先输出当前节点的名字,然后递归遍历它的子节点。Stream API提供了forEach方法,对于Collection类型的节点可以直接使用,对于非Collection类型的节点,可以使用Stream.of(node)将其转换为Stream类型,然后再使用forEach方法。 实际上,这也是Stream API的一大特点:它非常适合用来遍历集合和树形结构,尤其是当我们需要对集合或做一些操作时,可以避免使用for循环,使代码更加简洁和优雅。 ### 回答3: Java 8的Stream是一种非常强大的API,可以极大地简化代码,并且可以用于遍历树形结构。在Java 8中,我们可以使用Stream来进行树形结构的遍历,这会使代码更加简洁、易读、易于理解和维护。 首先我们需要通过递归来遍历树形结构。在遍历过程中,我们可以将每个节点转换为Stream,然后通过flatMap方法来实现对树形结构的遍历。例如,如果我们有一个树形结构的数据结构Node,其中每个节点都可以包含多个子节点,我们可以通过如下的方式进行遍历: ```java public class Node { List<Node> subNodes; //其他属性 public Stream<Node> stream() { return Stream.concat(Stream.of(this), subNodes.stream().flatMap(Node::stream)); } } ``` 这里我们定义了一个stream方法,该方法返回一个Stream<Node>,它包含了当前节点和所有子节点的Stream。我们使用Stream.concat方法将当前节点添加到子节点的Stream中,并通过flatMap方法将所有子节点的Stream合并为一个。 有了stream方法,我们就可以轻松地遍历树形结构了。例如,如果我们需要找到树形结构中名称为“test”的节点,可以使用如下代码: ```java Node root = //获取树形结构的根节点 Optional<Node> result = root.stream() .filter(node -> "test".equals(node.getName())) .findFirst(); ``` 这里我们使用filter方法对每个节点进行过滤,只保留名称为“test”的节点,并使用findFirst方法返回第一个匹配的节点。如果我们需要获取所有名称为“test”的节点,可以使用如下代码: ```java List<Node> result = root.stream() .filter(node -> "test".equals(node.getName())) .collect(Collectors.toList()); ``` 这里我们使用collect方法将Stream转换为List,以获取所有匹配的节点。 总之,使用Java 8的Stream可以非常方便地遍历树形结构。我们可以将每个节点转换为Stream并通过flatMap方法合并为一个Stream,然后使用filter等方法对节点进行过滤和操作。这种做法可以大大简化代码,使代码更加易读、易于理解和维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值