springEl 构建通用树

        再项目开发中,需要构建组织的树形,菜单的树形,字典树形。感觉相似的代码写了一堆,就想着有没有办法,写个通用的方法去处理下?       

        学习了《SpringEL详解》,用springEl处理下。

构建树形:

菜单实体:

@Data
@ToString
public class Menu {private Integer id;private String name;private Integer parentId;private Integer sortId;private String url;private List<Menu> subMenus;public Menu(Integer id, String name, String url, Integer parentId) {this.id = id;this.name = name;this.url = url;this.parentId = parentId;this.subMenus = new ArrayList<>();}public Menu(Integer id, String name, Integer parentId) {this.id = id;this.name = name;this.parentId = parentId;this.subMenus = new ArrayList<>();}public Menu(Integer id, String name, Integer parentId, List<Menu> subMenus) {this.id = id;this.name = name;this.parentId = parentId;this.subMenus = subMenus;}
}

递归的方式:

    //  容易出现 大量无效的循环public static List<Menu> recursiveBuildTree(List<Menu> menus, int parentId) {List<Menu> result = new ArrayList<>();for (Menu menu : menus) {if (menu.getParentId().equals(parentId)) {// 递归查找子菜单menu.setSubMenus(recursiveBuildTree(menus, menu.getId()));result.add(menu);}}return result;}

递归的方式,还要主要限制层级和效率的问题

map的方式:

    public static List<Menu> buildTree(List<Menu> list, Integer matchParentId) {List<Menu> res = new ArrayList<>();// 创建一个Map,以id为键Map<Integer, List<Menu>> parentMenuMap = new HashMap<>();Integer parentId;for (Menu menu : list) {parentId = menu.getParentId();parentMenuMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(menu);}// 遍历列表,将每个节点添加到其父节点的children列表中for (Menu menu : list) {menu.setSubMenus(parentMenuMap.getOrDefault(menu.getId(), new ArrayList<>()));// 从根节点开始构建树,假设根节点的parentId为0if (matchParentId.equals(menu.getParentId())) {res.add(menu);}}return res;}

测试:
 public static void main(String[] args) {List<Menu> listMapTree = initMenus();List<Menu> result = MapBuildTree.buildTree(listMapTree, -1);System.out.println(JSON.toJSONString(result));} public static List<Menu> initMenus(){List<Menu> datas = new ArrayList<>();Menu root = new Menu(1, "root", "/", -1);Menu leaf2 = new Menu(2, "leaf1", "/", 1);Menu leaf3 = new Menu(3, "leaf2", "/", 1);Menu leaf4 = new Menu(4, "leaf11", "/api/v1/leaf11.html", 2);Menu leaf5 = new Menu(5, "leaf12", "/api/v1/leaf12.html", 2);datas.add(root);datas.add(leaf2);datas.add(leaf3);datas.add(leaf4);datas.add(leaf5);return datas;}

通用的方式

递归的方式:

过程:

1,获取到父级id 用于匹配

2,设置子项

     

3,获取id,用于递归

el 例子- 获取值:
public static void main(String[] args) {Map<String, Object> myMap = new HashMap<>();myMap.put("name", "yan");myMap.put("id", 1222);myMap.put("position", "E");ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = new StandardEvaluationContext();// 设置变量context.setVariable("myMap", myMap);String value1 = parser.parseExpression("#myMap['name']").getValue(context, String.class);System.out.println(value1);Integer value2 = parser.parseExpression("#myMap['id']").getValue(context, Integer.class);System.out.println(value2);String value3 = parser.parseExpression("#myMap['position']").getValue(context, String.class);System.out.println(value3);}
el 例子-设置值:
 
public static void main(String[] args) {List<Map<String, Object>> listMapTree = initMenuMap();ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = new StandardEvaluationContext();String variableName ="myMap";Map<String, Object> mapList = new HashMap<>();mapList.put("id", 22);mapList.put("parentId", -1);mapList.put("children", new ArrayList<>());// 设置变量// 设置上下文中的变量context.setVariable(variableName, mapList);parser.parseExpression("#"+variableName+"['children']").setValue(context, listMapTree);System.out.println(JSON.toJSONString(mapList));}public static List<Map<String,Object>> initMenuMap(){return changeFormat(initData());}private static List<Map<String,Object>> changeFormat(String areaInfo){JSONArray areaArr = JSONArray.parseArray(areaInfo);return ListUtils.emptyIfNull(areaArr).stream().map(e -> (JSONObject) e).map(e -> (Map<String, Object>)JSONObject.parseObject( e.toJSONString())).collect(Collectors.toList());}public static String initData(){return "[{\"id\":1,\"name\":\"root\",\"parentId\":-1,\"subMenus\":[],\"url\":\"/\"},{\"id\":2,\"name\":\"leaf1\",\"parentId\":1,\"subMenus\":[],\"url\":\"/\"},{\"id\":3,\"name\":\"leaf2\",\"parentId\":1,\"subMenus\":[],\"url\":\"/\"},{\"id\":4,\"name\":\"leaf11\",\"parentId\":2,\"subMenus\":[],\"url\":\"/api/v1/leaf11.html\"},{\"id\":5,\"name\":\"leaf12\",\"parentId\":2,\"subMenus\":[],\"url\":\"/api/v1/leaf12.html\"}]";}

el 递归处理:
public static<T, E> List<T> buildTree(List<T> trees, String parentIdName,String idName, String childrenName, E matchParentId) {String variableName ="myMap";String parentIdVariName = "#" + variableName + "['" + parentIdName + "']";String childrenVariName = "#"+variableName+"['"+childrenName+"']";String idVariName = "#" + variableName + "['" + idName + "']";return buildTrees(trees, parentIdVariName, idVariName, childrenVariName, matchParentId, variableName);}public static<T, E> List<T> buildTrees(List<T> trees, String parentIdVariName,String idVariName, String childrenVariName, E matchParentId, String variableName ) {ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = new StandardEvaluationContext();List<T> result = new ArrayList<>();for (T node : trees) {//添加到下级数据中context.setVariable(variableName, node);E parentId = (E) parser.parseExpression(parentIdVariName).getValue(context);System.out.println(parentId);if(parentId == matchParentId){E id = (E) parser.parseExpression(idVariName).getValue(context);parser.parseExpression(childrenVariName).setValue(context, buildTrees(trees,parentIdVariName, idVariName, childrenVariName, id, variableName));result.add(node);}}return result;}
测试:
 public static void main(String[] args) {List<Menu> listMapTree = initMenus();List<Menu> result2 = RecurElBuildTree.buildTree(listMapTree, "parentId", "id", "subMenus", -1);System.out.println(JSON.toJSONString(result2));}

map的方式:

过程:

1,获取到父级id 用于生成map对象

2,获取id,设置子项

     

3,再获取父节id,根据表达式判断是否是父级

4,表达式可能多个点话,内容进行替换

 el-表达式:
 public static void main(String[] args) {ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = new StandardEvaluationContext();String variableName ="myMap";Map<String, Object> mapList = new HashMap<>();mapList.put("id", 22);mapList.put("parentId", -1);mapList.put("children", new ArrayList<>());// 设置变量// 设置上下文中的变量context.setVariable(variableName, mapList);Integer parentId = parser.parseExpression("#"+variableName+"['parentId']").getValue(context, Integer.class);System.out.println(parentId);Boolean equal  = parser.parseExpression(parentId +" == -1 || " + parentId+" == null").getValue(context, Boolean.class);System.out.println("express: " + equal);}
map处理:
import cn.hutool.core.util.StrUtil; 
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class TreeElUtil {public static<T, E> List<T> buildTreee(List<T> trees, String parentIdName,String idName, String childrenName, String parentIdExr) {ExpressionParser parser = new SpelExpressionParser();EvaluationContext context = new StandardEvaluationContext();String variableName ="myMap";// 创建一个Map,以id为键Map<E, List<T>> parentMenuMap = new HashMap<>();String parentIdVariName = "#" + variableName + "['" + parentIdName + "']";for (T node : trees) {context.setVariable(variableName, node);E parentId = (E) parser.parseExpression(parentIdVariName).getValue(context);parentMenuMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(node);}List<T> result = new ArrayList<>();String childrenVariName = "#"+variableName+"['"+childrenName+"']";String idVariName = "#" + variableName + "['" + idName + "']";for (T node : trees) {//添加到下级数据中context.setVariable(variableName, node);E id = (E) parser.parseExpression(idVariName).getValue(context);
//            parser.parseExpression(childrenName).setValue(node, parentMenuMap.getOrDefault(id, new ArrayList<>()));parser.parseExpression(childrenVariName).setValue(context, parentMenuMap.getOrDefault(id, new ArrayList<>()));//如里是根节点,加入结构E parentId = (E) parser.parseExpression(parentIdVariName).getValue(context);// 这边的String format = formatResult(parentIdExr, parentId);Boolean parent  = parser.parseExpression(format).getValue(context, Boolean.class);if (parent != null && parent) {result.add(node);}}return result;}private static String formatResult(String str, Object... param){String regExp = "\\{}";Pattern pattern = Pattern.compile(regExp);Matcher matcher = pattern.matcher(str);while(matcher.find()){str = StrUtil.format(str, param);;}return str;}}

测试:

map数据:

    public static void main(String[] args) {List<Map<String, Object>> listMapTree = initOrgMaps();String parentExr= "{} == -1 || {} == null";List<Map<String, Object>> result = TreeElUtil.buildTreee(listMapTree, "parentOrgId", "orgId", "subOrgs", parentExr);System.out.println(JSON.toJSONString(result));}public static List<Map<String, Object>> initOrgMaps(){List<Map<String, Object>> datas = new ArrayList<>();Map<String, Object> root = generateMap(1, "主部门", -1);Map<String, Object> leaf2 = generateMap(2, "业务部门", 1);Map<String, Object> leaf3 = generateMap(3, "开发部门", 1);Map<String, Object> leaf4 = generateMap(4, "业务部门11", 2);Map<String, Object> leaf5 = generateMap(5, "业务部门12", 2);datas.add(root);datas.add(leaf2);datas.add(leaf3);datas.add(leaf4);datas.add(leaf5);return datas;}public static Map<String, Object> generateMap(Integer orgId, String name, Integer parentOrgId){Map<String, Object> map = new HashMap<>();map.put("orgId", orgId);map.put("parentOrgId", parentOrgId);map.put("name", name);map.put("subOrgs", new ArrayList<>());return map;}

bean格式数据:

org:
@Data
@ToString
public class Org {private Integer orgId;private String name;private Integer parentOrgId;private Integer sortId;private List<Org> subOrgs;public Org(Integer orgId, String name, Integer parentOrgId) {this.orgId = orgId;this.parentOrgId = parentOrgId;this.name = name;this.subOrgs = new ArrayList<>();}
}
测试:
public static void main(String[] args) {List<Org> listMapTree = initOrgs();String parentExr= "{} == -1 || {} == null";List<Org> result = TreeElUtil.buildTreee(listMapTree, "parentOrgId", "orgId", "subOrgs", parentExr);System.out.println(JSON.toJSONString(result));}public static List<Org> initOrgs(){List<Org> datas = new ArrayList<>();Org root = new Org(1, "主部门", -1);Org leaf2 = new Org(2, "业务部门", 1);Org leaf3 = new Org(3, "开发部门", 1);Org leaf4 = new Org(4, "业务部门11", 2);Org leaf5 = new Org(5, "业务部门12", 2);datas.add(root);datas.add(leaf2);datas.add(leaf3);datas.add(leaf4);datas.add(leaf5);return datas;}

总结:

        如果相似的代码出现多次,就可以考虑做一个通用的处理。SpringEL的功能很强大,不过这个用java8的特性去处理,看起来更直观。

        学习了SpringEl,就想办法多应用,遇到不懂的再学习,这样才会学得更深,才容易举一反三。

        关联文章:《java8 构建通用树》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/66853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 入门速通-第1章【黑马】

内容来源于&#xff1a;黑马 集成开发环境&#xff1a;CLion CLion的官方下载网址&#xff1a;CLion: A Cross-Platform IDE for C and C by JetBrains 我在b站找到了一个安装教程&#xff0c;可以按照这个视频教程进行安装&#xff08;内置汉化教程&#xff09;&#xff1a; …

Python的进程和线程

ref 接受几个设定: 进程是一家almost密不透风的公司,缅甸KK园区 线程里面工作的…人 进程**[园区]**内公共资源对于进程来说,可以共享. 别的园区[进程],一般不能和自己的园区共享人员资源,除非… 好的,现在再接受设定: 单个CPU在任一时刻只能执行单个线程&#xff0c;只有…

自动化01

测试用例的万能公式&#xff1a;功能测试界面测试性能测试易用性测试安全性测试兼容性测试 自动化的主要目的就是用来进行回归测试 新产品--第一个版本 (具备丰富的功能)&#xff0c;将产品的整体进行测试&#xff0c;人工创造一个自动化测试用例&#xff0c;在n个版本的时候…

jmeter中对接口进行循环请求后获取相应数据

1、工作中遇到一个场景就是对某个单一接口进行循环请求&#xff0c;并需要获取每次请求后返回的相应数据&#xff1b; 2、首先就在jmeter对接口相关组件进行配置&#xff0c;需要组件有&#xff1a;循环控制器、CSV数据文件设置、计数器、访问接口、HTTP信息头管理器、正则表达…

Jetson Xavier NX 安装 CUDA 支持的 PyTorch 指南

本指南将帮助开发者完成在 Jetson Xavier NX 上安装 CUDA 支持的 PyTorch。 安装方法 在 Jetson 上安装 Pytorch 只有两种方法。 一种是直接安装他人已经编译好的 PyTorch 轮子&#xff1b;一种是自己从头开始开始构建 PyTorch 轮子并且安装。 使用轮子安装 可以从我的 Gi…

Ansible fetch模块详解:轻松从远程主机抓取文件

在自动化运维的过程中&#xff0c;我们经常需要从远程主机下载文件到本地&#xff0c;以便进行分析或备份。Ansible的fetch模块正是为了满足这一需求而设计的&#xff0c;它可以帮助我们轻松地从远程主机获取文件&#xff0c;并将其保存到本地指定的位置。在这篇文章中&#xf…

【AI论文】生成式视频模型是否通过观看视频学习物理原理?

摘要&#xff1a;AI视频生成领域正经历一场革命&#xff0c;其质量和真实感在迅速提升。这些进步引发了一场激烈的科学辩论&#xff1a;视频模型是否学习了能够发现物理定律的“世界模型”&#xff0c;或者&#xff0c;它们仅仅是复杂的像素预测器&#xff0c;能够在不理解现实…

论文速读|Matrix-SSL:Matrix Information Theory for Self-Supervised Learning.ICML24

论文地址&#xff1a;Matrix Information Theory for Self-Supervised Learning 代码地址&#xff1a;https://github.com/yifanzhang-pro/matrix-ssl bib引用&#xff1a; article{zhang2023matrix,title{Matrix Information Theory for Self-Supervised Learning},author{Zh…

视觉语言模型 (VLMs):跨模态智能的探索

文章目录 一. VLMs 的重要性与挑战&#xff1a;连接视觉与语言的桥梁 &#x1f309;二. VLMs 的核心训练范式&#xff1a;四种主流策略 &#x1f5fa;️1. 对比训练 (Contrastive Training)&#xff1a;拉近正例&#xff0c;推远负例 ⚖️2. 掩码方法 (Masking)&#xff1a;重构…

数据结构——堆(介绍,堆的基本操作、堆排序)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

机器学习-线性回归(简单回归、多元回归)

这一篇文章&#xff0c;我们主要来理解一下&#xff0c;什么是线性回归中的简单回归和多元回归&#xff0c;顺便掌握一下特征向量的概念。 一、简单回归 简单回归是线性回归的一种最基本形式&#xff0c;它用于研究**一个自变量&#xff08;输入&#xff09;与一个因变量&…

Git知识分享

一、理解git首先要理清楚下面五个概念&#xff1a; 1、工作区(git add 命令之前的样子) 2、stash 暂存(暂存工作区和暂存区的更改) 3、暂存区(git add 命令之后的存储区, 4、本地仓库(git commit提交的位置) 5、远程仓库(git push提交的位置) 二、git常用命令&#xff1a; 1、g…

2024年度技术总结——MCU与MEMS和TOF应用实践

引言 2024年对我来说是技术成长与突破的一年。在这一年里&#xff0c;我不仅在技术领域拓展了深度和广度&#xff0c;还通过与客户合作的实际项目&#xff0c;成功实现了从单一MCU到MCU、MEMS与TOF技术融合的跨越。这一过程中&#xff0c;我深刻认识到&#xff0c;技术的进步不…

一句话,我让 AI 帮我做了个 P 图网站!

每到过节&#xff0c;不少小伙伴都会给自己的头像 P 个图&#xff0c;加点儿装饰。 比如圣诞节给自己头上 P 个圣诞帽&#xff0c;国庆节 P 个小红旗等等。这是一类比较简单、需求量却很大的 P 图场景&#xff0c;也有很多现成的网站和小程序&#xff0c;能帮你快速完成这件事…

如何打造一个高并发系统?

今天和大家聊聊作为一个后端开发&#xff0c;在实际工作中&#xff0c;我们如何打造一个高并发的系统&#xff1f; 如下图所示&#xff0c;大概有六个层面&#xff0c;我们结合具体的场景直播间签到去一一细说。 一、前端 1、打散请求&#xff1a;即把用户的接口分散一点去请求…

996引擎 - 前期准备-配置开发环境

996引擎 - 前期准备 官网搭建服务端、客户端单机搭建 开发环境配置后端开发环境配置环境 前端开发环境配置环境 后端简介前端简介GUILayoutGUIExport 官网 996传奇引擎官网 所有资料从官网首页开始&#xff0c;多探索。 文档&#xff1a; 996M2-服务端Lua 996M2-客户端Lua 搭…

迅为RK3568开发板篇OpenHarmony实操HDF驱动控制LED-添加内核编译

编译内核时将该 HDF 驱动编译到镜像中&#xff0c;接下来编写驱动编译脚本 Makefile&#xff0c;代码如下所示&#xff1a; 加入编译体系&#xff0c;填加模块目录到 drivers/hdf_core/adapter/khdf/linux/Makefile 文件 更多内容可以关注&#xff1a;迅为RK3568开发板篇OpenHa…

生信软件管家——conda vs pip

pip vs conda&#xff1a; 安装过python包的人自然两种管理软件都用过&#xff0c; Pip install和Conda install在Python环境中用于安装第三方库和软件包&#xff0c;但它们在多个方面存在显著的区别 总的来说&#xff1a; pip是包管理软件&#xff0c;conda既是包管理软件&…

ubuntu 布暑python项目

在Ubuntu上部署Python项目通常包括以下几个步骤&#xff1a; 1 安装必要的软件&#xff1a; 确保系统已经安装了Python、pip&#xff08;Python包管理工具&#xff09;以及virtualenv&#xff08;可选&#xff0c;用于创建独立的Python环境&#xff09;。如果还没有安装&#…

RV1126画面质量一:视频基础

在聊视频画面调节之前&#xff0c;先来认识一下视频画面的有一些基础问题 如今我们所处的时代&#xff0c;是移动互联网时代&#xff0c;也可以说是 视频时代 。 从快播到抖音&#xff0c;从“ 三生三世 ” 到 “ 三十而已 ” &#xff0c;我们的生活&#xff0c;被越来越多的 …