java-Stream原理及相关操作详解(filter、map、flatMap、peek、reduce、anyMatch等等)

java-Stream原理及相关操作详解

  • Stream流前言
  • Stream流原理介绍
  • Stream-Api常用方法介绍
      • filter()
      • map()
      • flatMap
      • peek
      • reduce
      • max、min
      • findAny、 findFirst
      • allMatch、anyMatch、noneMatch
      • sorted
      • count

Stream流前言

Java8特性主要是Stream流以及函数式接口的出现;本片文章主要对StreamApi中的常用方法进行讲解,再学习Stream流之前个人建议需要对函数式接口要有充分理解并能熟练掌握,对于函数式编程的学习大家可以参考:java 函数式编程(Function、Supplier、Predicate、Consumer)
本片文章主要讲解流相关的操作以及部分原理介绍;

Stream流原理介绍

Stream流本质上是一个执行计算的过程,将流当中的中间操作组成一个流管道的形式,流管道由源(可能是数组,集合,生成器函数,I / O通道等),零个或多个中间操作 (将流转换为另一个流,如filter(Predicate) )组成,以及终端操作 (产生结果或副作用,例如count()、forEach(Consumer) 、collect(?); 对于collect()方法来讲是Stream的重中之重,collect()方法的实现依赖于Collector,对于流的终端收集操作(Collector)会放在后续进行讲解;
流本身是由三个部分组成,分别是源、中间操作、终止操作;大家先看个例子:

int sum = widgets.stream().filter(w -> w.getColor() == RED).mapToInt(w -> w.getWeight()).sum();

widgets认为是一个集合本身的引用,通过widgets.stream()则创建了一个源,中间的filter()和mapToInt() 操作是流的中间操作过程,sum是流的一个终端终止操作;
大家再来观察一下stream()方法;

    default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);}public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {Objects.requireNonNull(spliterator);return new ReferencePipeline.Head<>(spliterator,StreamOpFlag.fromCharacteristics(spliterator),parallel);}

stream() 是 Collection 中的 default 方法,实际上调用的是StreamSupport.stream() 方法,返回的是 ReferencePipeline.Head的实例;ReferencePipeline表示的是流阶段的中间阶段ReferencePipeline.Head表示流的源阶段;一个流的只会创建一次源,而流的中间操作可以没有或者多个;ReferencePipeline.Head 的构造函数传递是 ArrayList 中实现的 spliterator 。常用的集合都实现了 Spliterator 接口以支持 Stream。可以这样理解,Spliterator 定义了数据集合流入流水线的方式。

接下来将对Stream的构造方法也就是流的的创建过程以及中间操作阶段的具体实现进行分析,来进一步理解流的创建以及流是怎么将每一个中间阶段链接起来并且怎么将每一个阶段的输出通知给下游流; 我们先分析stream()方法的最终实现:其实是AbstractPipeline,而ReferencePipeline继承了AbstractPipeline:

    AbstractPipeline(Spliterator<?> source,int sourceFlags, boolean parallel) {//表示上一个阶段,如果这是源阶段,也就是流创建的阶段并没有上游所以这里为null。this.previousStage = null;//流管道中的源拆分器;实际行为是和sourceSupplier配合将集合中的元素驶向流this.sourceSpliterator = source;//流管道的源阶段this.sourceStage = this;// 此管道对象表示的中间操作的操作标志this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;// The following is an optimization of:// StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);//源和所有操作的组合源标志和操作标志,直到此流水线对象表示的操作为止(包括该流水线对象所代表的操作)。 在管道准备进行评估时有效。this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;//如果是顺序的,则此管道对象与流源之间的中间操作数;如果是并行的,则为先前有状态的中间操作数。 在管道准备进行评估时有效this.depth = 0;//表示并行还是串行流this.parallel = parallel;}

对于stream()的构造方法我认为previousStage 、sourceStage 、depth 三个属性即可,能更快捷的去了解流;
构造方法执行完成之后流的创建也完成了。创建了源;

  • previousStage 并没有上一个阶段,赋予null,
  • sourceStage 原阶段就是当前创建的流对象;
  • depth 为0,我们目前并没有执行下游流的操作;

好了~到目前为止,流的创建阶段也就完成了;我们再看看下游流是怎么创建的呢?(拿filter()举例)

    @Overridepublic final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {Objects.requireNonNull(predicate);// 返回一个匿名无状态操作的管道return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,StreamOpFlag.NOT_SIZED) {@Override// 下游生产线所需要的回调接口Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {@Overridepublic void begin(long size) {downstream.begin(-1);}// 真正执行操作的方法,依靠ChainedReference内置ReferencePipeline引用下游的回调@Overridepublic void accept(P_OUT u) {// 只有满足条件的元素才能被下游执行if (predicate.test(u))downstream.accept(u);}};}};}

StatelessOp是非常重要的,定义了流中无状态操作的属性;我这里直接点击到了StatelessOp中具体的构造方法大家再看看一下:

/**
* previousStage入参表示流对象,我们上步骤创建好的流对象
*/AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {if (previousStage.linkedOrConsumed)throw new IllegalStateException(MSG_STREAM_LINKED);// 表示如果该流管道被消费或者链接则为true,上面我们创建了stream()流,调用filter方法则表示我们将filter这个阶段已经连接到流previousStage.linkedOrConsumed = true;// 流管道的下一个阶段,上步骤创建好的流对象下一个对象就是当前对象previousStage.nextStage = this;// 此时该流管道中的上一个阶段便是源阶段this.previousStage = previousStage;this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);// 源阶段只有一个仍然是previousStage.sourceStage;this.sourceStage = previousStage.sourceStage;if (opIsStateful())sourceStage.sourceAnyStateful = true;// 新增了一个filter操作,深度便+1;this.depth = previousStage.depth + 1;}

在和源阶段的构造方法和下游流创建的构造方法拿出来对比会发现:

  • previousStage.linkedOrConsumed = true表示上面我们创建了stream()流,调用filter方法则表示我们将filter这个阶段已经连接到流
  • previousStage(上一个阶段) :对于源的创建来讲没有上一个阶段,该属性为null,对于下游流来讲上一个阶段就是原阶段;
  • linkedOrConsumed (是否消费或链接):流创建阶段没有被链接或者消费默认为fasle,创建下游流之后被链接上之后为true
  • previousStage.nextStage:上游流的下个阶段,可以理解为源的下一个阶段便是filter
  • depth ,源阶段没有任何中间操作未创建下游流,depth = 0,创建链接之后便+1;

其实发现previousStage.nextStage = this previousStage.linkedOrConsumed = this; 通过连接上游流和指向下游流发现流的特性是很像一个双向链表

到目前为止,流的创建(源阶段以及中间阶段)就结束了,接下来我们再看一下ReferencePipeline.Head 代表流的源看看数据是怎么走向的呢?我们来观察一下ReferencePipeline.Head类:

    /*** Source stage of a ReferencePipeline.** @param <E_IN> type of elements in the upstream source  * @param <E_OUT> type of elements in produced by this stage* @since 1.8*/static class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> {//方法目前已经省略便于观看;}

其中有两个泛型参数E_IN、E_OUT,显而易见一个表示进,一个表示出,E_IN表示的是上游源的元素的类型,E_OUT表示的是当前阶段所生成的元素类型

比如我们使用steam: stream().map().filter()… 对于map()阶段来说,E_IN则是stream生成的源(Spliterator),E_OUT表示map()处理完之后生成的元素,这个元素可能是对象、集合等等。 对于filter()来说 filter()的输入元素就是map()所输出的元素,filter()的输出元素则会作为下一个中间动作的输入元素。。。。

流的创建以及数据流向已经介绍完了,接下来我们简单介绍流的每一个阶段是怎么进行通知的:Sink

  • Sink 这个接口,它继承自 Consumer 接口,又定义了begin()、end()、cancellationRequested() 方法。Sink 直译过来是水槽,如果把数据流比作水,那水槽就是水会流过的地方。begin() 用于通知水槽的水要过来了,里面会做一些准备工作,同样 end() 是做一些收尾工作。cancellationRequested() 是原来判断是不是可以停下来了。Consumer 里的accept() 是消费数据的地方。

  • 每一个 Sink 都有自己的职责,但具体表现各有不同。无状态操作的 Sink 接收到通知或者数据,处理完了会马上通知自己的 下游。有状态操作的 Sink 则像有一个缓冲区一样,它会等要处理的数据处理完了才开始通知下游,并将自己处理的结果传递给下游。例如 sorted() 就是一个有状态的操作,一般会有一个属于自己的容器,用来记录处自己理过的数据的状态。sorted() 是在执行 begin 的时候初始化这个容器,在执行 accept 的时候把数据放到容器中,最后在执行 end 方法时才正在开始排序。排序之后再将数据,采用同样的方式依次传递给下游节点。最后数据流到终止节点,终止节点将数据收集起来就结束了。

好了~ ~ 流我们有了一个基础理念、并掌握函数式接口之后我们再来回顾一下Stream中常用方法~~~

Stream-Api常用方法介绍

filter()

Stream<T> filter(Predicate<? super T> predicate);

在对Api讲解时,将不再对流的原理进行介绍,只关注方法本身的入参,出参以及使用;

可以看到filter入参是Predicate(接受一个参数不返回值) 出参仍返回一个Stream,方法本身返回由此流中满足给定的Predicate行为后元素组成的流;

举个例子:集合中有四个元素分别是字符类型"supplier", “function”, “predicate”, “consumer”,需要将符合条件的元素捞取出来(其中符合元素的条件根据业务可调整)当前例子是将元素长度=8的元素过滤出来。。

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");list.stream().filter(item -> item.length() == 8).forEach(System.out::println);}打印结果为:supplier  function  consumer;结果中predicate元素长度等于9 不满足Predicate行为;

map()

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

返回一个流,包括将给定函数应用到该流元素的结果。 入参是一个Function(接受一个参数并返回值),出参是Stream
对于map()方法的使用我们需要将集合中的元素进行处理,处理完之后并返回新的结果时,便可使用map

举个例子:

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");//将list中每一个元素末尾加“~~”list.stream().map(item -> item + "~~").forEach(System.out::println);}结果打印:supplier~~ function~~ predicate~~ consumer~~

flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

将集合中的每一个元素铺开汇聚成新结果以Stream的形式返回

举个例子:有两个集合list1、list2 铺开形成一个list

    @Testvoid stream(){// 有两个集合list1、list2需要两个集合中的元素长度等于10的元素收集起来并返回新的集合List<String> list1 = Arrays.asList("supplier1", "function1", "predicate1", "consumer1");List<String> list2 = Arrays.asList("supplier2", "function2", "predicate2", "consumer2");List<List<String>> list = Arrays.asList(list1, list2);List<String> collect = list.stream().flatMap(Collection::stream).filter(item -> item.length() == 10).collect(Collectors.toList());collect.forEach(System.out::println);}输出结果:predicate1  predicate2

这样使用起来大家觉得可能用处比较少不属于常用方法,但对于oracle来讲对于批量查询如果数据过多会报错,需要对数据进行分片,分片之后在进行查询;
写一个方法大家应该就理解了

    /*** @param list     string的列表,根据这个去数据库查* @param function 具体的查询方法* @param <T>      返回的list的对象* @return 返回数据库查出的具体方法*/public static <T> List<T> partitionThousandGet(List<String> list, Function<List<String>, List<T>> function) {return Lists.partition(list, 1000).stream().map(function).flatMap(Collection::stream).collect(Collectors.toList());}

其中使用flatMap便可执行分片查询功能,在function参数中传入指定的dao层方法即可;

peek

Stream<T> peek(Consumer<? super T> action);

peek的意思是偷看,入参是Consumer 返回一个Stream,返回由该流的元素组成的流,并在所提供的流中执行所提供的每个元素上的动作
举一个例子:

学生类有名字和分数两个属性;
@Data
public class Student {private String name;private int score;
}@Test
void stream(){Student student1 = new Student("zhangsan", 21);Student student2 = new Student("lisi", 82);Student student3 = new Student("wangwu", 32);// 分别有三个学生;需要将lisi的分数改为100分List<Student> list = Arrays.asList(student1, student3, student2);list.stream().peek(item -> {if (item.getName().equals("lisi")) {item.setScore(100);}}).forEach(System.out::println);
}
打印结果为:
Student(name=zhangsan, score=21)
Student(name=wangwu, score=32)
Student(name=lisi, score=100)

reduce

/**T identity,输入元素BinaryOperator<T> accumulator 累加器,接受两个相同结果的入参返回一个值;BinaryOperator<U> combiner  合成器,在使用并行流时使用;
*/
T reduce(T identity, BinaryOperator<T> accumulator);Optional<T> reduce(BinaryOperator<T> accumulator);<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

reduce本身时汇聚的一个过程,可以将目标元素通过累加器合成最终目标结果元素;reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。
举个例子

	学生类有名字和分数两个属性;@Datapublic class Student {private String name;private int score;}@Testvoid stream(){Student student1 = new Student("zhangsan", 21);Student student2 = new Student("lisi", 82);Student student3 = new Student("wangwu", 32);//需要计算学生分数总和List<Student> list = Arrays.asList(student1, student3, student2);int sum = list.stream().mapToInt(Student::getScore).reduce(Integer::sum).getAsInt();System.out.println(sum);}

max、min

    Optional<T> max(Comparator<? super T> comparator);Optional<T> min(Comparator<? super T> comparator);

两个方法入参都是Comparator,出参是Optional对象;返回流中最大或者最小的元素
举个例子:

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");//获取流中最大的元素String maxString = list.stream().max(String::compareToIgnoreCase).get();System.out.println(maxString);}打印:supplier

findAny、 findFirst

    Optional<T> findAny();Optional<T> findFirst();
  • findFirst(): findFirst()方法返回流中的第一个元素(根据流的顺序)。它在并行流操作中的行为更可预测,通常会返回流的第一个元素。如果流为空,则返回一个空的Optional对象。
  • findAny(): findAny()方法返回流中的任意一个元素。它对于并行流操作提供了更好的性能,因为它可以在多个线程中并行地搜索元素。不同的运行时环境可能会对元素的选择有所不同。如果流为空,则返回一个空的Optional对象。

当流具有确定顺序的情况下(例如使用List创建的流),findFirst()和findAny()通常返回相同的结果。但是,在并行流操作中,由于并行执行的特性,findAny()可能更适合并行化操作,而且在某些情况下可能具有更好的性能。

举个例子:

    @Testvoid stream(){//对于串行流来讲有序集合两个方法返回的都是同一个元素List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");String string1 = list.stream().findFirst().get();String string2 = list.stream().findAny().get();System.out.println(string1 + "--" + string2);}输出:supplier--supplier 

但是对于并行流来讲findAny会随机返回一个元素:

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");String string1 = list.parallelStream().findFirst().get();String string2 = list.parallelStream().findAny().get();System.out.println(string1 + "--" + string2);}打印:supplier--predicate

allMatch、anyMatch、noneMatch

boolean allMatch(Predicate<? super T> predicate);
boolean anyMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);

三个方法都是接受一个Predicate参数返回boolean值;三个操作都是Stream中的终止操作

  • allMatch 用于检查流中所有元素是否满足判断条件 如果都满足返回true 其中有一个不满足返回false;
  • anyMatch用于检查流中至少有一个元素满足判断条件,当流中至少有一个元素满足时,返回ture,否则false
  • 验证流中元素没有一个满足条件时返回true

我们举个例子来说明:

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");String str = "supplier";boolean anyMatchResult = list.stream().anyMatch(item -> item.equals(str));boolean allMatchResult = list.stream().allMatch(item -> item.equals(str));boolean noneMatchResult = list.stream().noneMatch(item -> item.equals(str));System.out.println("anyMatchResult结果:" +anyMatchResult+ " allMatchResult结果:" + allMatchResult + " noneMatchResult结果: "+noneMatchResult);}运行结果为:anyMatchResult结果:true allMatchResult结果:false noneMatchResult结果:false

我们可以看到对于anyMatch方法 集合中有一个元素等于我们定义的字符串supplier,条件成立返回true,对于allMatch并不是所有元素都等于我们定义的字符串supplier,条件不成立返回false;对于noneMatch来讲并不是所有元素都不符合条件,返回false;

sorted

	Stream<T> sorted();Stream<T> sorted(Comparator<? super T> comparator);
  • sorted()将元素从小到大排序
    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");list.stream().sorted().forEach(System.out::println);}输出	   consumerfunctionpredicatesupplier
  • sorted(Comparator<? super T> comparator) 比较器自定义排序
    @Testvoid stream(){//根据字符串长度进行排序List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");list.stream().sorted(Comparator.comparing(String::length)).forEach(System.out::println);}输出:supplierfunctionconsumerpredicate

count

返回流中元素的个数
举个例子:

    @Testvoid stream(){List<String> list = Arrays.asList("supplier", "function", "predicate", "consumer");long count = list.stream().count();System.out.println(count);}打印:4

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

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

相关文章

【Java设计模式】创建型——工厂方法模式

目录 背景/问题解决方案思路方案 图解简单工厂模式/静态工厂模式工厂方法模式 代码示例&#xff1a;图形工厂意图主要解决何时使用如何解决关键代码 工厂模式的优点工厂模式的缺点使用场景注意事项 背景/问题 在软件设计中&#xff0c;我们经常遇到需要创建不同类型对象的情况…

主成分分析相关的方法

数据降维是指通过保留数据的重要信息&#xff0c;将高维数据转换为低维数据的过程。这有助于减少数据的复杂性、提高计算效率、消除噪音等。以下是一些常见的数据降维方法&#xff1a; 1、主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;&#xff1a; …

torchvision中的数据集使用

torchvision中的数据集使用 使用和下载CIFAR10数据集 输出测试集中的第一个元素&#xff08;输出img信息和target&#xff09; 查看分类classes 打断点–>右键Debug–>找到classes 代码 import torchvisiontrain_set torchvision.datasets.CIFAR10(root"./data…

实现点击用户头像或者id与其用户进行聊天(vue+springboot+WebSocket)

用户点击id直接与另一位用户聊天 前端如此&#xff1a; <template><!-- 消息盒子 --><div class"content-box" :style"contentWidth"><!-- 头像&#xff0c;用户名 --><div class"content-box-top box--flex">&l…

36.基于SpringBoot + Vue实现的前后端分离-高校汉服租赁网站系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的高校汉服租赁网站系统设计与实现管理…

【数据库】SQL简介

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理关系型数据库管理系统&#xff08;RDBMS&#xff09;的标准化语言。它用于访问和操作数据库中的数据&#xff0c;执行各种任务&#xff0c;如插入、更新、删除和检索数据&#x…

阿里云8核32G云服务器租用优惠价格表,包括腾讯云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

c#编程基础学习之基本语句

目录 if...else语句If...Else 简写语句(三元运算符)switch语句while循环Do/While 循环for循环foreach 循环break和continue语句 if…else语句 C# 条件语句用于根据不同条件执行不同动作。 a < b a < b a > b a > b a b a ! bC# 具有以下条件语句&#xff1a; 使…

SpringBoot3整合RabbitMQ之二_简单队列模型案例

SpringBoot3整合RabbitMQ之二_简单队列模型案例 文章目录 SpringBoot3整合RabbitMQ之二_简单队列模型案例1. 简单队列模型1. 消息发布者1. 创建简单队列的配置类2. 发布消费Controller 2. 消息消费者3. 输出结果 1. 简单队列模型 简单队列模型就是点对点发布消息&#xff0c;有…

虚拟机VMware启动虚拟机刚启动有网之后没网

虚拟机VMware启动虚拟机刚启动有网之后没网 害&#xff0c;感觉这种调试的事情是真的浪费时间 如题&#xff0c;对于这种情况&#xff0c;一句话&#xff0c;就是你本地的DHCP虚拟机服务以及NAT网络服务没启动 本机windowR,输入services.msc 进入服务 然后 喏&#xff0c;…

ffmpeg 从现有视频中截取一段

ffmpeg 从现有视频中截取一段 References ffmpeg -i ./input.mp4 -vcodec copy -acodec copy -ss 00:00:10 -to 00:00:25 ./output.mp4 -y strongforeverstrong:~/ForeverStrong$ ffmpeg -i ./train_video.mp4 -vcodec copy -acodec copy -ss 00:00:10 -to 00:00:25 ./output…

重读Java设计模式: 适配器模式解析

引言 在软件开发中&#xff0c;经常会遇到不同接口之间的兼容性问题。当需要使用一个已有的类&#xff0c;但其接口与我们所需的不兼容时&#xff0c;我们可以通过适配器模式来解决这一问题。适配器模式是一种结构型设计模式&#xff0c;它允许接口不兼容的类之间进行合作。本…

Redis实现网站访问人数统计

在网站开发中&#xff0c;统计网站访问人数是非常重要的&#xff0c;它可以帮助我们了解网站的流量情况&#xff0c;评估网站的受欢迎程度&#xff0c;并且可以用于广告定价、流量分析等。在这篇博客中&#xff0c;我们将讨论如何使用Redis实现简单的网站访问人数统计&#xff…

golang字符串排序:数字优先,英文其次,中文按照拼音排序

golang字符串排序&#xff1a;数字优先&#xff0c;英文其次&#xff0c;中文按照拼音排序 目的&#xff1a;数字优先&#xff0c;英文其次&#xff0c;中文按照拼音排序思路&#xff1a;将字符串转为统一的编码格式&#xff08;GBK&#xff09;再排序代码示例 目的&#xff1a…

Unity类银河恶魔城学习记录12-6.5 p128.5 Create item by Craft源代码

此章节在原视频缺失&#xff0c;此过程为根据源代码推断而来&#xff0c;并非原视频步骤 Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩…

yolov8实现用已经训练好的模型去实现数据集的自动标注

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、找到default.yaml文件二、修改default.yaml文件三、代码实现四、生成结果展示补充&#xff08;呼应前面代码训练数据集的路径位置&#xff09; 前言 我们经…

机器学习笔记 - 文字转语音技术路线简述以及相关工具不完全清单

一、TTS技术简述 今天的文本到语音转换技术(TTS)的目标已经不仅仅是让机器说话,而是让它们听起来像不同年龄和性别的人类。通常,TTS 系统合成器的质量是从不同方面进行评估的,包括合成语音的清晰度、自然度和偏好,以及人类感知因素,例如可理解性。 1、技术路线 (1)基…

【多模态融合】MetaBEV 解决传感器故障 3D检测、BEV分割任务

前言 本文介绍多模态融合中&#xff0c;如何解决传感器故障问题&#xff1b;基于激光雷达和相机&#xff0c;融合为BEV特征&#xff0c;实现3D检测和BEV分割&#xff0c;提高系统容错性和稳定性。 会讲解论文整体思路、模型框架、论文核心点、损失函数、实验与测试效果等。 …

详解 Redis 在 Centos 系统上的安装

文章目录 详解 Redis 在 Centos 系统上的安装1. 使用 yum 安装 Redis 52. 创建符号链接3. 修改配置文件4. 启动和停止 Redis 详解 Redis 在 Centos 系统上的安装 1. 使用 yum 安装 Redis 5 如果是Centos8&#xff0c;yum 仓库中默认的 redis 版本就是5&#xff0c;直接 yum i…

【Python】免费的图片/图标网站

专栏文章索引&#xff1a;Python 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 这里是我收集的几个免费的图片/图标网站&#xff1a; iconfont-阿里巴巴矢量图标库icon&#xff08;.ico&#xff09;INCONFINDER&#xff08;.ico&#xff09;