解密阿里大神写的天书般的Tree工具类,轻松搞定树结构!

首发公众号:赵侠客

一、引言

最近公司新进了不少新人,包括一些来自阿里、网易等大型企业的资深工程师。我们组的一位新同事是阿里来的专家,我在CR(Code Review, 简称CR)时看到了他编写的一个关于树操作的工具类,对其设计和实现深感佩服。为了进一步理解和学习,我对这个工具类进行了深入分析和整理,现在在本文中与大家分享。

二、树形结构介绍

2.1 简单的二叉树

首页简单简介一下树形数据结构,树形数据结构是一种层级化的数据组织方式,广泛用于表示具有层次关系的数据。由于其层级化组织特点,树形数据结构能够高效支持多种查找、插入和删除操作,因此在计算机科学和实际应用中得到了广泛应用。下面是一个简单的二叉树示例:

二叉树及遍历算法

▲二叉树及遍历算法

2.2 树的应用场景

树形数据结构的应用场景是通过ID关联上下级关系的对象,然后将这些对象组织成一棵树。主要有以下常用应用场景:

  1. 部门通讯录:通讯录中可以通过树形结构展示不同部门及其上下级关系,便于用户快速找到联系人
  2. 系统菜单: 系统中的菜单通常是分层的,通过树形结构可以方便地展示和管理各级菜单项
  3. 地址选择器:地理地址通常有多级关系,如省、市、县,通过树形结构可以方便用户选择具体的地址
  4. 文件夹目录: 文件系统中的文件夹和文件可以通过树形结构来组织和展示,便于用户进行文件操作
  5. 产品多级分类: 通过树形结构可以直观地展示和管理各级分类
  6. 评论回复: 通过树形结构可以展示帖子的回复关系,便于查看讨论的脉络

树形数据结构的应用场景通常是分层的,通过树形结构可以展示和管理各级流程节点及其关系。 这些场景中,树形结构的应用可以显著提升数据的组织和展示效率,帮助用户更直观地理解和操作系统。

树形结构在电商分类中的应用

▲:树形结构在电商分类中的应用

三、JAVA中的树形数据结构

3.1 JAVA树形结构对象定义

在JAVA中树形结构是通过对象嵌套方式来定义的,如MenuVo对象中有一个子对象subMenus:

@Data
public class MenuVo {private Long id;private Long pId;private String name;private Integer rank=0;private List<MenuVo> subMenus=new ArrayList<>();
}

3.2 JSON数据格式中的树形结构

JSON数据天然就是树形结果,如以下展示一个简单的JSON树形结构:

[{"id": 0,"subMenus": [{"id": 2,"subMenus": [{"id": 5,"pid": 2}],"pid": 0}],"pid": -1}
]

3.3 树形数据结构的储存

像文档型数据库MongDB、ElasticSearch可以直接存储JSON这种树形数据,而像Mysql这种关系型数据库不太适合直接存储具有上下级关系的树形数据,大都是按行存储然后通过id、pid之间关联上下级关系,这就导致我们需要经常将Mysql中的关系型数据转成JSON这种数据结构,因而TreeUtil这类数据转换工具类就起诞生了。

数据库中存储的城市数据结构

▲:数据库中存储的城市数据结构

四、TreeUtil代码分析

4.1 makeTree()构建树

直接看这神一样的方法makeTree():

public class TreeUtil {public static <E> List<E> makeTree(List<E> list, Predicate<E> rootCheck, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> setSubChildren) {return list.stream().filter(rootCheck).peek(x -> setSubChildren.accept(x, makeChildren(x, list, parentCheck, setSubChildren))).collect(Collectors.toList());}private static <E> List<E> makeChildren(E parent, List<E> allData, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> setSubChildren) {return allData.stream().filter(x -> parentCheck.apply(parent, x)).peek(x -> setSubChildren.accept(x, makeChildren(x, allData, parentCheck, setSubChildren))).collect(Collectors.toList());}
}

是不是完全看不懂?像看天书一样?makeTree方法为了通用使用了泛型+函数式编程+递归,正常人一眼根本看不这是在干什么的,我们先不用管这个makeTree合成树的代码原理,先直接看如何使用:

MenuVo menu0 = new MenuVo(0L, -1L);
MenuVo menu1 = new MenuVo(1L, 0L);
MenuVo menu2 = new MenuVo(2L, 0L);
MenuVo menu3 = new MenuVo(3L, 1L);
MenuVo menu4 = new MenuVo(4L, 1L);
MenuVo menu5 = new MenuVo(5L, 2L);
MenuVo menu6 = new MenuVo(6L, 2L);
MenuVo menu7 = new MenuVo(7L, 3L);
MenuVo menu8 = new MenuVo(8L, 3L);
MenuVo menu9 = new MenuVo(9L, 4L);
//基本数据
List<MenuVo> menuList = Arrays.asList(menu0,menu1, menu2,menu3,menu4,menu5,menu6,menu7,menu8,menu9);
//合成树
List<MenuVo> tree= TreeUtil.makeTree(menuList, x->x.getPId()==-1L,(x, y)->x.getId().equals(y.getPId()), MenuVo::setSubMenus);
System.out.println(JsonUtils.toJson(tree));

我们结合这个简单的合成菜单树看一下这个makeTree()方法参数是如何使用的:

  1. 第1个参数List list,为我们需要合成树的List,如上面Demo中的menuList
  2. 第2个参数Predicate rootCheck,判断为根节点的条件,如上面Demo中pId==-1就是根节点
  3. 第3个参数parentCheck 判断为父节点条件,如上面Demo中 id==pId
  4. 第4个参数setSubChildren,设置下级数据方法,如上面Demo中: Menu::setSubMenus

有了上面这4个参数,只要是合成树场景,这个TreeUtil.makeTree()都可以适用,比如我们要合成一个部门树:

@Data
public class GroupVO {private String groupId;private String parentGroupId;private String groupName;private List<GroupVO> subGroups=new ArrayList<>();
}

groupId是部门ID, 根部门的条件是parentGroupId=null, 那么调用合成树的方法为:

List<GroupVO> groupTree=TreeUtil.makeTree(groupList, x->x.getParentGroupId==null,(x, y)->x.getGroupId().equals(y.getParentGroupId), GroupVO::setSubGroups);

是不是很优雅?很通用?完全不需要实现什么接口、定义什么TreeNode、增加什么TreeConfig,静态方法直接调用就搞定。 一个字:绝!

五、神方法拆解

5.1 去掉泛型和函数接口

第一步我们可以把泛型和函数接口去掉,再看一下一个如何使用递归合成树:

    public static List<MenuVo> makeTree(List<MenuVo> allDate,Long rootParentId) {List<MenuVo> roots = new ArrayList<>();// 1、获取所有根节点for (MenuVo menu : allDate) {if (Objects.equals(rootParentId, menu.getPId())) {roots.add(menu);}}// 2、所有根节点设置子节点for (MenuVo root : roots) {makeChildren(root, allDate);}return roots;}public static MenuVo makeChildren(MenuVo root, List<MenuVo> allDate) {//遍历所有数据,获取当前节点的子节点for (MenuVo menu : allDate) {if (Objects.equals(root.getId(), menu.getPId())) {makeChildren(menu, allDate);//将是当前节点的子节点添加到当前节点的subMenus中root.getSubMenus().add(menu);}}return root;}

调用方法:

       List<MenuVo> tree2 = parseTree(menuList,-1L);

通过上面的两个方法可以合成树的基本逻辑,主要分为三步

  1. 找到所有根节点
  2. 遍历所有根节点设置子节点
  3. 遍历allDate查询子节点

5.2 使用函数优化

看懂上面的代码后,我们再给加上函数式接口:

    public static List<MenuVo> makeTree(List<MenuVo> allDate, Predicate<MenuVo> rootCheck, BiFunction<MenuVo, MenuVo, Boolean> parentCheck, BiConsumer<MenuVo, List<MenuVo>> setSubChildren) {// 1、获取所有根节点List<MenuVo> roots = allDate.stream().filter(x->rootCheck.test(x)).collect(Collectors.toList());;// 2、所有根节点设置子节点roots.stream().forEach(x->makeChildren(x,allDate,parentCheck,setSubChildren));return roots;}public static MenuVo makeChildren(MenuVo root, List<MenuVo> allDate,BiFunction<MenuVo, MenuVo, Boolean> parentCheck, BiConsumer<MenuVo, List<MenuVo>> setSubChildren) {//遍历所有数据,获取当前节点的子节点allDate.stream().filter(x->parentCheck.apply(root,x)).forEach(x->{makeChildren(x, allDate,parentCheck,setSubChildren);//将是当前节点的子节点添加到当前节点的subMenus中setSubChildren.accept(x,allDate);});return root;}

结合前面的方式再来看这个函数式接口是不是简单多了,只是写法上函数化了而已。使用函数优化的整体结构和最终的方法有点像了,最后再使用泛型优化就成了最终版本。从这个例子来看代码还是要不断优化的,大神可以直接写出神一样的代码,小弟一步步优化,一点点进步也是能写出大神一样的代码的。

六、其它操作Tree方法

6.1 遍历Tree

学习过二叉树都知道遍历二叉树有先序、中序、后序、层序,如果这些不清楚的可以先去学习一下,针对Tree的遍历这里提供了三个方法: 先序forPreOrder(),后序forPostOrder(),层序forLevelOrder():


public static <E> void forPreOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> getSubChildren) {for (E l : tree) {consumer.accept(l);List<E> es = getSubChildren.apply(l);if (es != null && es.size() > 0) {forPreOrder(es, consumer, getSubChildren);}}
}public static <E> void forLevelOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> getSubChildren) {Queue<E> queue = new LinkedList<>(tree);while (!queue.isEmpty()) {E item = queue.poll();consumer.accept(item);List<E> childList = getSubChildren.apply(item);if (childList != null && !childList.isEmpty()) {queue.addAll(childList);}}
}public static <E> void forPostOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> getSubChildren) {for (E item : tree) {List<E> childList = getSubChildren.apply(item);if (childList != null && !childList.isEmpty()) {forPostOrder(childList, consumer, getSubChildren);}consumer.accept(item);}
}

我们看测试方法:

//先序
StringBuffer preStr=new StringBuffer();
TreeUtil.forPreOrder(tree,x-> preStr.append(x.getId().toString()),Menu::getSubMenus);
Assert.assertEquals("0137849256",preStr.toString());//层序
StringBuffer levelStr=new StringBuffer();
TreeUtil.forLevelOrder(tree,x-> levelStr.append(x.getId().toString()),Menu::getSubMenus);
Assert.assertEquals("0123456789",levelStr.toString());//后序
StringBuffer postOrder=new StringBuffer();
TreeUtil.forPostOrder(tree,x-> postOrder.append(x.getId().toString()),Menu::getSubMenus);
Assert.assertEquals("7839415620",postOrder.toString());

通过这个Demo我们解释一下遍历中的几个参数:

  1. tree 需要遍历的树,就是makeTree()合成的对象
  2. Consumer consumer 遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 postOrder.append(x.getId().toString())
  3. Function<E, List> getSubChildren,获取下级数据方法,如Menu::getSubMenus

有了这三个方法遍历Tree是不是和遍历List一样简单方便了?二个字:绝了!!

6.2 flat打平树

我们可以将一个List使用markTree()构建成树,就可以使用flat()将树还原成List

    public static <E> List<E> flat(List<E> tree, Function<E, List<E>> getSubChildren, Consumer<E> setSubChildren) {List<E> res = new ArrayList<>();forPostOrder(tree, item -> {setSubChildren.accept(item);res.add(item);}, getSubChildren);return res;}

使用方法:

    List<Menu> flat = TreeUtil.flat(tree, Menu::getSubMenus,x->x.setSubMenus(null));Assert.assertEquals(flat.size(),menuList.size());flat.forEach(x->{Assert.assertTrue(x.getSubMenus()==null);});

flat()参数解释:

  1. tree 需要打平的树,就是makeTree()合成的对象
  2. Function<E, List> getSubChildren,获取下级数据方法,如Menu::getSubMenus
  3. Consumer setSubChildren,设置下级数据方法,如: x->x.setSubMenus(null)

6.3 sort()排查

我们知道针对List,可以使用list.sort()直接排序,那么针对树,就可以调用sort()方法直接对树中所有子节点直接排序:

    public static <E> List<E> sort(List<E> tree, Comparator<? super E> comparator, Function<E, List<E>> getChildren) {for (E item : tree) {List<E> childList = getChildren.apply(item);if (childList != null && !childList.isEmpty()) {sort(childList,comparator,getChildren);}}tree.sort(comparator);return tree;}

比如MenuVo有一个rank值,表明排序权重

        MenuVo menu0 = new MenuVo(0L, -1L);MenuVo menu1 = new MenuVo(1L, 0L);menu1.setRank(100);MenuVo menu2 = new MenuVo(2L, 0L);menu2.setRank(1);MenuVo menu3 = new MenuVo(3L, 1L);MenuVo menu4 = new MenuVo(4L, 1L);MenuVo menu5 = new MenuVo(5L, 2L);menu5.setRank(5);MenuVo menu6 = new MenuVo(6L, 2L);MenuVo menu7 = new MenuVo(7L, 3L);menu7.setRank(5);MenuVo menu8 = new MenuVo(8L, 3L);menu8.setRank(1);MenuVo menu9 = new MenuVo(9L, 4L);List<MenuVo> menuList = Arrays.asList(menu0,menu1, menu2,menu3,menu4,menu5,menu6,menu7,menu8,menu9);//合成树List<MenuVo> tree= TreeUtil.makeTree(menuList, x->x.getPId()==-1L,(x, y)->x.getId().equals(y.getPId()), MenuVo::setSubMenus);System.out.println(JsonUtils.toJson(tree));

如查我们想按rank正序:

List<MenuVo> sortTree= TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus);

如果我们想按rank倒序:

List<MenuVo> sortTree= TreeUtil.sort(tree, (x,y)->y.getRank().compareTo(x.getRank()), MenuVo::getSubMenus);

sort参数解释:

  1. tree 需要排序的树,就是makeTree()合成的对象
  2. Comparator<? super E> comparator 排序规则Comparator,如:Comparator.comparing(MenuVo::getRank)按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序
  3. Function<E, List> getChildren 获取下级数据方法,如:MenuVo::getSubMenus

这个给树排序是不是和对List排序一样的简单:三个字:太绝了!!!

七、总结

看完这位大神编写的TreeUtil工具类后,我深感佩服,其设计与实现真是令人叹为观止。该工具类不仅优雅且高效,使得以往需要递归处理的树形结构操作变得更加简洁和便捷。未来处理树形数据时,只需直接使用该工具类即可,无需再编写复杂的递归代码。

最后附完成代码方便CV工程师,还不赶快点赞、关注、收藏


/*** @Description: 树操作方法工具类* @Author: 公众号:赵侠客* @Copyright: Copyright (c) 赵侠客* @Date: 2024-07-22 10:42* @Version: 1.0*/
public class TreeUtil {/*** 将list合成树** @param list 需要合成树的List* @param rootCheck 判断E中为根节点的条件,如:x->x.getPId()==-1L , x->x.getParentId()==null,x->x.getParentMenuId()==0* @param parentCheck 判断E中为父节点条件,如:(x,y)->x.getId().equals(y.getPId())* @param setSubChildren   E中设置下级数据方法,如: Menu::setSubMenus* @param <E>  泛型实体对象* @return   合成好的树*/public static <E> List<E> makeTree(List<E> list, Predicate<E> rootCheck, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> setSubChildren) {return list.stream().filter(rootCheck).peek(x -> setSubChildren.accept(x, makeChildren(x, list, parentCheck, setSubChildren))).collect(Collectors.toList());}/***  将树打平成tree* @param tree  需要打平的树* @param getSubChildren  设置下级数据方法,如: Menu::getSubMenus,x->x.setSubMenus(null)* @param setSubChildren 将下级数据置空方法,如: x->x.setSubMenus(null)* @return  打平后的数据* @param <E> 泛型实体对象*/public static <E> List<E> flat(List<E> tree, Function<E, List<E>> getSubChildren, Consumer<E> setSubChildren) {List<E> res = new ArrayList<>();forPostOrder(tree, item -> {setSubChildren.accept(item);res.add(item);}, getSubChildren);return res;}/*** 前序遍历** @param tree 需要遍历的树* @param consumer  遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素* @param setSubChildren  设置下级数据方法,如: Menu::getSubMenus,x->x.setSubMenus(null)* @param <E> 泛型实体对象*/public static <E> void forPreOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> setSubChildren) {for (E l : tree) {consumer.accept(l);List<E> es = setSubChildren.apply(l);if (es != null && es.size() > 0) {forPreOrder(es, consumer, setSubChildren);}}}/*** 层序遍历** @param tree 需要遍历的树* @param consumer 遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素* @param setSubChildren 设置下级数据方法,如: Menu::getSubMenus,x->x.setSubMenus(null)* @param <E> 泛型实体对象*/public static <E> void forLevelOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> setSubChildren) {Queue<E> queue = new LinkedList<>(tree);while (!queue.isEmpty()) {E item = queue.poll();consumer.accept(item);List<E> childList = setSubChildren.apply(item);if (childList != null && !childList.isEmpty()) {queue.addAll(childList);}}}/*** 后序遍历** @param tree 需要遍历的树* @param consumer 遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素* @param setSubChildren 设置下级数据方法,如: Menu::getSubMenus,x->x.setSubMenus(null)* @param <E> 泛型实体对象*/public static <E> void forPostOrder(List<E> tree, Consumer<E> consumer, Function<E, List<E>> setSubChildren) {for (E item : tree) {List<E> childList = setSubChildren.apply(item);if (childList != null && !childList.isEmpty()) {forPostOrder(childList, consumer, setSubChildren);}consumer.accept(item);}}/*** 对树所有子节点按comparator排序** @param tree 需要排序的树* @param comparator  排序规则Comparator,如:Comparator.comparing(MenuVo::getRank)按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序* @param getChildren 获取下级数据方法,如:MenuVo::getSubMenus* @return 排序好的树* @param <E> 泛型实体对象*/public static <E> List<E> sort(List<E> tree, Comparator<? super E> comparator, Function<E, List<E>> getChildren) {for (E item : tree) {List<E> childList = getChildren.apply(item);if (childList != null && !childList.isEmpty()) {sort(childList,comparator,getChildren);}}tree.sort(comparator);return tree;}private static <E> List<E> makeChildren(E parent, List<E> allData, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> children) {return allData.stream().filter(x -> parentCheck.apply(parent, x)).peek(x -> children.accept(x, makeChildren(x, allData, parentCheck, children))).collect(Collectors.toList());}
}

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

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

相关文章

操作系统:进程1

一.进程 1.什么是进程 一个进程创建&#xff0c;他会生成几块&#xff1a; 代码段&#xff1a;进程执行的程序代码数据段&#xff1a;全局变量&#xff0c;静态变量&#xff0c;在进程生命周期中是动态可变的堆&#xff1a;动态分配的内存区域&#xff0c;malloc、calloc、real…

html实现酷炫美观的可视化大屏(十种风格示例,附源码)

文章目录 完整效果演示1.蓝色流线风的可视化大屏1.1 大屏效果1.2 大屏代码1.3 大屏下载 2.地图模块风的可视化大屏2.1 大屏效果2.2 大屏代码2.3 大屏下载 3.科技轮动风的可视化大屏3.1 大屏效果3.2 大屏代码3.3 大屏下载 4.蓝色海洋风的可视化大屏4.1 大屏效果4.2 大屏代码4.3 …

快速介绍git(Linux)

git 1、安装2、版本控制3、git vs gitee&&GitHub(git故事)4、git的操作 1、安装 很简单&#xff0c;直接 sudo yum install -y git2、版本控制 故事介绍&#xff1a;你是一个大学生&#xff0c;你上课需要交一分实验报告&#xff0c;教你的老师比较负责&#xff0c;…

手把手教你集成GraphRag.Net:打造智能图谱搜索系统

在人工智能和大数据发展的背景下&#xff0c;我们常常需要在项目中实现知识图谱的应用&#xff0c;以便快速、准确地检索和使用信息。 今天&#xff0c;我将向大家详细介绍如何在一个新的.NET项目中集成GraphRag.Net&#xff0c;这是一个参考GraphRag实现的.NET版本&#xff0c…

Linux_make/Makefile的理解

1.make是一个命令&#xff0c;makefile是一个文件, 依赖关系和依赖方法. a.快速使用一下 i.创建一个Makefile文件(首字母也可以小写) b.依赖关系和依赖方法 i.依赖关系: 我为什么要帮你? mybin:mytest.c ii.依赖方法: 怎么帮? gcc -o mybin mytest.c make之前要注意先创建…

探索LLM世界:新手小白的学习路线图

随着人工智能的发展&#xff0c;语言模型&#xff08;Language Models, LLM&#xff09;在自然语言处理&#xff08;NLP&#xff09;领域的应用越来越广泛。对于新手小白来说&#xff0c;学习LLM不仅能提升技术水平&#xff0c;还能为职业发展带来巨大的机遇。那么&#xff0c;…

Linux(虚拟机)的介绍

Linux介绍 常见的操作系统 Windows&#xff1a;微软公司开发的一款桌面操作系统&#xff08;闭源系统&#xff09;。版本有dos&#xff0c;win98&#xff0c;win NT&#xff0c;win XP , win7, win vista. win8, win10&#xff0c;win11。服务器操作系统&#xff1a;winserve…

conda issue

Conda 是一个跨平台、通用的二进制包管理器。它是 Anaconda 安装使用的包管理器&#xff0c;但它也可能用于其他系统。Conda 完全用 Python 编写&#xff0c;并且是 BSD 许可的开源。通用意味着大部分的包都可以用它进行管理&#xff0c;很像一个跨平台版本的apt或者yum&#x…

vue3 父组件 props 异步传值,子组件接收不到或接收错误

1. 使用场景 我们在子组件中通常需要调用父组件的数据&#xff0c;此时需要使用 vue3 的 props 进行父子组件通信传值。 2. 问题描述 那么此时问题来了&#xff0c;在使用 props 进行父子组件通信时&#xff0c;因为数据传递是异步的&#xff0c;导致子组件无法成功获取数据…

汇川CodeSysPLC教程03-2-6 ModBus TCP

什么是ModBus TCP&#xff1f; ModBus TCP是一种基于TCP/IP协议的工业网络通信协议&#xff0c;常用于工业自动化和控制系统。它是ModBus协议的一个变种&#xff0c;ModBus协议最初由Modicon&#xff08;现在是施耐德电气的一部分&#xff09;在1979年开发。 以下是ModBus TC…

数据治理之“财务一张表”

前言 信息技术的发展&#xff0c;伴随企业业务系统的纷纷建设&#xff0c;提升业务处理效率的同时&#xff0c;也将企业的整体主价值链流程分成了一段一段的业务子流程&#xff0c;很多情况下存在数据上报延迟、业务协作不顺畅、计划反馈不及时、库存积压占资多……都可以从数据…

【Android】linux

android系统就是跑在linux上的系统。Linux层里面包含系统和硬件驱动等一些本地代码的环境。 linux的目录 mount: 用于查看哪个模块输入只读&#xff0c;一般显示为&#xff1a; [rootlocalhost ~]# mount /dev/cciss/c0d0p2 on / type ext3 (rw) proc on /proc type proc (…

Spring AI (五) Message 消息

5.Message 消息 在Spring AI提供的接口中&#xff0c;每条信息的角色总共分为三类&#xff1a; SystemMessage&#xff1a;系统限制信息&#xff0c;这种信息在对话中的权重很大&#xff0c;AI会优先依据SystemMessage里的内容进行回复&#xff1b; UserMessage&#xff1a;用…

PlatformIO+ESP32S3学习:驱动WS2812矩阵彩灯显示FFT音律拾音灯

本文继承自之前的彩灯驱动文章&#xff1a;https://blog.csdn.net/qq_51930953/article/details/140736628 本文完成的效果&#xff1a; 1. 硬件准备 1.1. WS2812矩阵彩灯 购买地址&#xff1a;WS2812B全彩软像素屏8X8 8X32 16X16幻彩5V显示可编程像素软屏 1.2. 麦克风模块 购…

Ip2region - 基于xdb离线库的Java IP查询工具提供给脚本调用

文章目录 Pre效果实现git clone编译测试程序将ip2region.xdb放到指定目录使用改进最终效果 Pre OpenSource - Ip2region 离线IP地址定位库和IP定位数据管理框架 Ip2region - xdb java 查询客户端实现 效果 最终效果 实现 git clone git clone https://github.com/lionsou…

YOLOV8源码解读-C2f模块-以及总结c2模块、Bottleneck

c2f模块是对c2模块的改进 c2模块图解解读 先给出YOLOV8中卷积的定义模块一键三连-卷积-BN-激活函数 def autopad(k, pNone, d1): # kernel, padding, dilation"""Pad to same shape outputs."""if d > 1:k d * (k - 1) 1 if isinstance…

Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)

上次介绍了&#xff1a;(Linux&#xff1a;进程信号&#xff08;一.认识信号、信号的产生及深层理解、Term与Core&#xff09;)[https://blog.csdn.net/qq_74415153/article/details/140624810] 文章目录 1.信号保存1.1递达、未决、阻塞等概念1.2再次理解信号产生与保存1.3信号…

Pytorch深度学习实践(9)卷积神经网络

卷积神经网络 全连接神经网络 神经网络中全部是线性模型&#xff0c;是由线性模型串联起来的 全连接网络又叫全连接层 卷积神经网络 在全连接神经网络中&#xff0c;由于输入必须是一维向量&#xff0c;因此在处理图像时必须要对图像矩阵进行拉伸成一维的形式&#xff0c;…

【算法】布隆过滤器

一、引言 在现实世界的计算机科学问题中&#xff0c;我们经常需要判断一个元素是否属于一个集合。传统的做法是使用哈希表或者直接遍历集合&#xff0c;但这些方法在数据量较大时效率低下。布隆过滤器&#xff08;Bloom Filter&#xff09;是一种空间效率极高的概率型数据结构&…

【NPU 系列专栏 2.8 -- 特斯拉 FDS NPU 详细介绍 】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 特斯拉 NPU 芯片介绍FSD(Full Self-Driving)芯片 简介FSD主要特点FSD 详细参数FSD 应用场景特斯拉 Hardware 3.0 芯片 简介Hardware 3.0主要特点Hardware 3.0 详细参数Hardware 3.0应用场景特斯拉自研 NPU 的优势优化设计高度…