java构建树形结构的实现过程

 更新时间:2025年11月10日 09:11:13   作者:xingweI2021  
文章介绍了五种构建树形结构的方式,包括定义实体类、利用Map集合、使用Stream流、基于Hutool以及MyBatis-Plus的@TableName属性,最后,还提供了一个基于Java实现树结构模糊搜索功能的示例

构建树形结构

废话少说,直接上代码

方式一

1、定义实体类

public class Category {

    private Long id;
    private String name;
    private Long parentId;
    private List<Category> children;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public List<Category> getChildren() {
        return children;
    }

    public void setChildren(List<Category> children) {
        this.children = children;
    }

    public Category(Long id, String name, Long parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }
}

工具类

private static List<Category> categories = new ArrayList<>();

static {
    // 绑定初始化一个Category的数据
    categories.add(new Category(1L, "一级分类-1", null));
    categories.add(new Category(2L, "一级分类-1-1", 1L));
    categories.add(new Category(3L, "一级分类-1-2", 1L));
    categories.add(new Category(30L, "一级分类-1-2-1", 3L));
    categories.add(new Category(4L, "一级分类-2", null));
    categories.add(new Category(5L, "一级分类-2-1", 4L));
    categories.add(new Category(6L, "一级分类-3", null));
    categories.add(new Category(7L, "一级分类-3-1", 6L));
    // 子节点
}

public static void main(String[] args) {
    String treeJson = JSONUtil.toJsonPrettyStr(parentList01());
    System.out.println(treeJson);
}

private static List<Category> parentList01(){
    List<Category> parentList = categories.stream().filter(category -> category.getParentId() == null).collect(Collectors.toList());
    for (Category category : parentList) {
        category.setChildren(buildChildren(category.getId(),categories));
    }
    return parentList;
}

private static List<Category> buildChildren(Long id, List<Category> categories) {
    List<Category> children = new ArrayList<>();
    for (Category category : categories) {
        if (category.getParentId() == null){
            continue;
        }
        if (category.getParentId().equals(id)){
            children.add(category);
        }
    }
    for (Category child : children) {
        child.setChildren(buildChildren(child.getId(),categories));
    }
    return children;
}

方式二

利用Map集合

@Override
public Result currentLeftMenu() {
    // 获取认证主体
    Subject subject = SecurityUtils.getSubject();
    ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
    Integer userId = activeUser.getSysUser().getId();
    // 根据用户ID 和 菜单权限查询 权限
    List<SysPermissionVO> sysPermissionVOS = sysPermissionMapper.selectUserPermission(userId, Constant.PERMISSION_TYPE_MENU);
    // 菜单容器
    Map<Integer, SysPermissionVO> menu = new HashMap<>();

    for (SysPermissionVO sysPermissionVO : sysPermissionVOS) {
        // 获取菜单中的父ID
        Integer parentId = sysPermissionVO.getParentId();
        // 判断父ID是否是一级菜单
        if (parentId.equals(Constant.MENU_LV1)) {
            sysPermissionVO.setChildren(new ArrayList<SysPermissionVO>());
            // key 菜单的ID value
            menu.put(sysPermissionVO.getId(), sysPermissionVO);
        }
    }
    // 给1一级菜单赋二级菜单
    for (SysPermissionVO sysPermissionVO : sysPermissionVOS) {
        Integer parentId = sysPermissionVO.getParentId();
        if (menu.containsKey(parentId)) {
            SysPermissionVO sysPermissionVO1 = menu.get(parentId);
            sysPermissionVO1.getChildren().add(sysPermissionVO);
        }
    }
    Collection<SysPermissionVO> values = menu.values();
    // 返回给controller
    return new Result(values);
}

方式三:stream流方式

public List<CategoryEntity> listWithTree() {
    //1、查出所有分类
    List<CategoryEntity> entities = baseMapper.selectList(null);

    //2、组装成父子的树形结构
    //2.1)、找到所有的一级分类,给children设置子分类
    return entities.stream()
        // 过滤找出一级分类
        .filter(categoryEntity -> categoryEntity.getParentCid() == 0)
        // 处理,给一级菜单递归设置子菜单
        .peek(menu -> menu.setChildren(getChildless(menu, entities)))
        // 按sort属性排序
        .sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
        .collect(Collectors.toList());
}

/**
 * 递归查找所有菜单的子菜单
 */
private List<CategoryEntity> getChildless(CategoryEntity root, List<CategoryEntity> all) {
    return all.stream()
        .filter(categoryEntity -> categoryEntity.getParentCid().equals(root.getCatId()))
        .peek(categoryEntity -> {
            // 找到子菜单
            categoryEntity.setChildren(getChildless(categoryEntity, all));
        })
        .sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
        .collect(Collectors.toList());
}

另一个

public List<CategoryEntity> listWithLambda() {

    List<CategoryEntity> categorys = baseMapper.selectList(Wrappers.<CategoryEntity>lambdaQuery().orderByDesc(CategoryEntity::getCatId))
    List<CategoryEntity> entities = new ArrayList<>(categorys);
    Map<Long, List<CategoryEntity>> longListNavigableMap = entities.stream().collect(Collectors.groupingBy(CategoryEntity::getParentCid));

    List<CategoryEntity> res = entities.stream().peek(entity -> {
        if (longListNavigableMap.containsKey(entity.getCatId())) {
            entity.setChildren(longListNavigableMap.get(entity.getCatId()));
        }
    }).filter(entity -> entity.getCatLevel() == 1).sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort()))).collect(Collectors.toList());

    return res;
}

方式四:基于Hutool进行构建

导入hutool的依赖,请参考hutool官网

实体类

@Data
@TableName("pms_category")
@AllArgsConstructor
public class CategoryEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 分类id
	 */
	@TableId
	private Long catId;
	/**
	 * 分类名称
	 */
	private String name;
	/**
	 * 父分类id
	 */
	private Long parentCid;
	/**
	 * 层级
	 */
	private Integer catLevel;
	/**
	 * 是否显示[0-不显示,1显示]
	 */
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;
	/**
	 * 排序
	 */
	private Integer sort;
	/**
	 * 图标地址
	 */
	private String icon;
	/**
	 * 计量单位
	 */
	private String productUnit;
	/**
	 * 商品数量
	 */
	private Integer productCount;

	/**
	 * 所有子分类
	 */
	@JsonInclude(JsonInclude.Include.NON_EMPTY)
	@TableField(exist = false)
	private List<CategoryEntity> children;

}

service

public class TreeUtils{
    public List<Tree<Long>> listWithTree() {
        ArrayList<CategoryEntity> dataList = Lists.newArrayList();
        dataList.add(new CategoryEntity(1L,"电子产品",0L,1,1,0,"/images/electronics.png","件",50,null));
        dataList.add(new CategoryEntity(2L,"手机通讯",1L,2,1,0,"/images/electronics.png","件",150,null));
        dataList.add(new CategoryEntity(3L,"家用电器",1L,2,1,0,"/images/electronics.png","件",250,null));
        List<CategoryEntity> entities = dataList;
        if (!CollectionUtil.isEmpty(entities)) {
            List<TreeNode<Long>> list = entities.
                    stream().
                    map(CategoryServiceImpl::getLongTreeNode).
                    collect(Collectors.toList());
            return TreeUtil.build(list, 0L);
        }
        return Collections.emptyList();
    }

    private static TreeNode<Long> getLongTreeNode(CategoryEntity item) {
	    TreeNode<Long> treeNode = new TreeNode<>();
	    treeNode.setId(item.getCatId());
	    treeNode.setParentId(item.getParentCid());
	    treeNode.setName(item.getName());
	    treeNode.setWeight(item.getSort());
	    Map<String, Object> extra = new HashMap<>();
	    extra.put("showStatus", item.getShowStatus());
	    extra.put("catLevel", item.getCatLevel());
	    extra.put("icon", item.getIcon());
	    extra.put("productUnit", item.getProductUnit());
	    extra.put("productCount", item.getProductCount());
	    treeNode.setExtra(extra);
	    return treeNode;
	}
} 

测试

public static void main(String[] args) {
    List<Tree<Long>> trees = listWithTree1();
    System.out.println(JSONUtil.toJsonPrettyStr(trees));
}

方式五

基于mybatisplus的@TableName里面的resultMap属性,当发生查询时,会根据配置的xml查询SQL进行查询

mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xw.mapper.FuncMapper">
    <resultMap id="thCloudFuncMap" type="com.xw.entity.Func">
        <id column="id" property="id"/>
        <result column="creator" property="creator"/>
        <result column="creator_name" property="creatorName"/>
        <result column="updater" property="updater"/>
        <result column="updater_name" property="updaterName"/>
        <result column="code" property="code"/>
        <result column="name" property="name"/>
        <result column="icon" property="icon"/>
        <result column="note" property="note"/>
        <result column="parent_id" property="parentId"/>
        <result column="sort" property="sort"/>
        <result column="component_name" property="componentName"/>
        <result column="type" property="type"/>
        <collection column="id" property="children" select="findByParentId" ofType="com.telehot.tpf.dev.rolemenu.entity.ThCloudFunc">
        </collection>
    </resultMap>


    <select id="findByParentId" resultMap="funcMap">
        SELECT * FROM func WHERE parent_id = #{parentId}
    </select>


    <select id="findAll" resultMap="funcMap">
        SELECT * FROM func where is_disabled = 0
    </select>
</mapper>

实体类

@TableName(value = "func", resultMap = "funcMap")
public class Func extends Entity {


    /**
     * 编码
     */
    private String code;

    /**
     * 菜单名称
     */
    private String name;

    /**
     * 图标
     */
    private String icon;

    /**
     * 图标
     */
    private String selectIcon;

    /**
     * 说明
     */

    private String note;

    /**
     * 父类ID
     */
    private Long parentId;

    /**
     * 排序
     */
    private Integer sort;

    /**
     * 类型
     */
    private Integer type;

    /**
     * 路径
     */
    private String path;

    /**
     * 应用id
     */
    private String appId;

    /***
     * 组件名
     */
    private String componentName;

    /**
     * 层级
     */
    private Integer tier;


    @TableField(exist = false)
    private List<Func> children;

}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 关于mybatis-plus逻辑删除自动填充更新时间的问题

    关于mybatis-plus逻辑删除自动填充更新时间的问题

    mybatis-plus是对mybatis的增强,mybatis-plus更像是面向对象编程,数据库基本CRUD的操作可以不用手动编写SQL语句,大大提高了开发的效率,这篇文章主要介绍了mybatis-plus逻辑删除自动填充更新时间问题,需要的朋友可以参考下
    2022-07-07
  • Spring MVC处理响应的案例详解

    Spring MVC处理响应的案例详解

    当服务器向客户端响应数据时,SpringMVC框架会使用“转换器”(Converter)将方法的返回值进行转换,SpringMVC框架还会自动使用不同的转换器,因此这篇文章就给大家详细介绍一下Spring MVC如何处理响应并附上案例,需要的朋友可以参考下
    2023-06-06
  • spring jpa集成依赖的环境准备及实体类仓库编写教程

    spring jpa集成依赖的环境准备及实体类仓库编写教程

    这篇文章主要为大家介绍了spring jpa集成依赖的环境准备及实体类仓库编写教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • Java集合 LinkedList的原理及使用详解

    Java集合 LinkedList的原理及使用详解

    这篇文章主要介绍了Java集合 LinkedList的原理及使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • JavaWeb使用POI操作Excel文件实例

    JavaWeb使用POI操作Excel文件实例

    这篇文章主要介绍了JavaWeb使用POI操作Excel文件,需要的朋友可以参考下
    2017-04-04
  • java基础之NIO介绍及使用

    java基础之NIO介绍及使用

    这篇文章主要介绍了java基础之NIO介绍及使用,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • java8从list集合中取出某一属性的值的集合案例

    java8从list集合中取出某一属性的值的集合案例

    这篇文章主要介绍了java8从list集合中取出某一属性的值的集合案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Java中的CyclicBarrier循环栅栏解析

    Java中的CyclicBarrier循环栅栏解析

    这篇文章主要介绍了Java中的CyclicBarrier循环栅栏解析,从字面上的意思可以知道,这个类的中文意思是"循环栅栏",大概的意思就是一个可循环利用的屏障,它的作用就是会让所有线程都等待完成后才会继续下一步行动,需要的朋友可以参考下
    2023-12-12
  • IDEA实用好用插件推荐及使用方法教程详解(必看)

    IDEA实用好用插件推荐及使用方法教程详解(必看)

    这篇文章主要介绍了IDEA实用好用插件推荐及使用方法教程,本文通过实例截图相结合给大家介绍的非常详细,对大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Java实现Excel文件转PDF(无水印无限制)

    Java实现Excel文件转PDF(无水印无限制)

    这篇文章主要为大家详细介绍了如何利用Java语言实现Excel文件转PDF的效果,并可以无水印、无限制。文中的示例代码讲解详细,需要的可以参考一下
    2022-06-06

最新评论