Java8新特性 Stream流详解

news2025/5/18 0:19:42

目录

1、介绍

2、获取Stream流的两种方式

方式一:根据Collection获取流

方式二:Stream中的静态方法of获取流

区别

3、Stream流注意事项

4、Stream流的常用方法

forEach

count

filter

limit

skip

map

sorted

distinct

match

find

max和min

reduce

map与reduce组合使用

mapToInt

concat

5、收集Stream流中的结果

将流中的数据收集到集合中

将流中的数据收集到数组中

对流中数据进行聚合计算

对流中数据进行分组

对流中数据进行分区

对流中数据进行拼接

6、并行的Stream流

获取并行Stream流的两种方式

第一种:直接获取并行的Stream流

第二种:将串行流转成并行流

串行与并行Stream代码对比

parallelStream线程安全问题

解决方案一:同步代码块

解决方案二:使用线程安全的集合

解决方案三:使用Collections.synchronizedList方法将集合转为线程安全的

解决方案四:调用Stream流的collect/toArray

parallelStream底层

Fork/join框架介绍

Fork/join原理-分治法

Fork/join原理-工作窃取算法


1、介绍

Stream流思想类似于工厂车间的流水线,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。

先看一段代码:我们会发现这样又循环又创建新的集合再存进去,很麻烦

public class Y {
    public static void main(String[] args) {
        List<String> nameList = new ArrayList<>();
        Collections.addAll(nameList,"张无忌","周芷若","赵敏","说不得","张乐");

        //拿到所有姓张的放进zhangList中
        List<String> zhangList = new ArrayList<>();
        List<String> threeList = new ArrayList<>();
        for(String name : nameList){
            if(name.startsWith("张")){
                zhangList.add(name);
            }
        }

        //遍历姓张的,拿出名字长度等于3的
        for(String name : zhangList){
            if(name.length() == 3){
                threeList.add(name);
            }
        }
        System.out.println(threeList);
    }
}

Stream流优化写法:

List<String> nameList = new ArrayList<>();
Collections.addAll(nameList,"张无忌","周芷若","赵敏","说不得","张乐");

nameList.stream()
    .filter((s) -> {
        return s.startsWith("张");
    })
    .filter((s) -> {
        return s.length() == 3;
    })
    .forEach((s) -> {
        System.out.println(s);
    });

2、获取Stream流的两种方式

方式一:根据Collection获取流

//方式一:根据Collection获取流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();

方式二:Stream中的静态方法of获取流

Stream<String> aa = Stream.of("张无忌","周芷若","赵敏","说不得","张乐");

区别

前者(of)是把集合当做一个整体处理,后者是把一个个元素分开来遍历。所以要对集合中每个元素做判断过滤,要用后者list.stream。

3、Stream流注意事项

(1)Stream只能操作一次

(2)Stream方法返回的是新的流

(3)Stream不调用终结方法,中间的操作不会执行

(4)如果Stream流的一些方法(limit、filter、skip等...)返回值是Stream,那么就得在最后调用终结方法(forEach、count)。

4、Stream流的常用方法

forEach

public class TestLambda2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"苏格拉底","柏拉图","亚里士多德","叔本华","尼采");
        //完整版
        list.stream().forEach((String str) -> {
            System.out.println(str);
        });

        //精简版
        list.stream().forEach(str -> System.out.println(str));
    }
}

count

统计其中的元素个数。

List<String> list = new ArrayList<>();
Collections.addAll(list,"苏格拉底","柏拉图","亚里士多德","叔本华","尼采");
//完整版
long count = list.stream().count();
System.out.println(count);

filter

用于过滤数据,返回符合条件的数据。

public class TestLambda2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"苏格拉底","柏拉图","亚里士多德","叔本华","尼采");
        //得到名字长度为3个字的人
        //完整版
        list.stream().filter(str -> {
           return str.length() == 3;
        }).forEach(System.out::println);

        //精简版
        list.stream().filter(str -> str.length() == 3).forEach(System.out::println);
    }
}

limit

可以进行截取,只取用前n个。

public class TestLambda2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"苏格拉底","柏拉图","亚里士多德","叔本华","尼采");

        //获取前3个元素
        list.stream().limit(3).forEach(System.out::println);
    }
}

skip

如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流。

public class TestLambda2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"苏格拉底","柏拉图","亚里士多德","叔本华","尼采");

        //获取前3个元素
        list.stream().skip(3).forEach(System.out::println);
    }
}

map

其实就是一种类型的流转换为另一种类型的流。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1","2","3");
        //将Stream流中的字符串转为Integer类型(完整版)
        stream.map((String str) -> {
            return Integer.parseInt(str);
        }).forEach(System.out::println);

        //简化版
        stream.map(str -> Integer.parseInt(str)).forEach(System.out::println);
    }
}

sorted

如果需要将数据排序,可以使用sorted方法。有如下两种方法API:

Stream<T> sorted();  //根据元素的自然顺序排序
Stream<T> sorted(Comparator<? super T> comparator); //根据比较器指定的规则排序
public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,2,5,1,8,7);
        //升序排列
        stream.sorted().forEach(System.out::println);
        //降序排列
        stream.sorted((i1,i2) -> i2 - i1).forEach(System.out::println);
    }
}

distinct

去除重复数据。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,1,1,8,8,7,10);
        //基本类型去重
        stream.distinct().forEach(System.out::println);
        //自定义类型去重(必须重写equals和hashcode方法)
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("杨玉环",20),
                new Person("李白",45),
                new Person("高力士",35));
        streamPerson.distinct().forEach(System.out::println);
    }
}

match

断言

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,1,8,7,10);
        //匹配所有元素,判断所有元素中是否都大于3
        boolean b = stream.allMatch(i -> i > 3);
        //匹配某个元素,只要有其中一个元素满足即可
        boolean b1 = stream.anyMatch(i -> i > 3);
        //匹配所有元素,所有元素都不满足条件
        boolean b2 = stream.noneMatch(i -> i > 3);
        System.out.println(b); //false
        System.out.println(b1); //true
        System.out.println(b2); //false
    }
}

find

找到某些数据。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,1,8,7,10);
        //找到第一个元素:6
        Optional<Integer> first = stream.findFirst();
        System.out.println(first.get()); //6
    }
}

max和min

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,1,8,7,10);
        Optional<Integer> max = stream.max((o1, o2) -> o1 - o2);
        System.out.println(max.get()); //10

        Optional<Integer> min = stream.min((o1, o2) -> o1 - o2);
        System.out.println(min.get()); //1
    }
}

reduce

如果需要将所有数据归纳得到一个数据,可以使用reduce方法。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(6,1,8,7,10);
        Integer reduce = stream.reduce(0, (x, y) -> {
            System.out.println("x = " + x + ",y = " + y);
            return x + y;
        });
        System.out.println(reduce); //32
    }
}

map与reduce组合使用

案例1:求出所有年龄的总和

public class TestLambda3 {
    public static void main(String[] args) {
        Integer reduce = Stream.of(
                new Person("李隆基", 40),
                new Person("杨玉环", 20),
                new Person("李白", 45),
                new Person("高力士", 35))
                .map((Person p) -> {
                    return p.getAge();
                }).reduce(0, (x, y) -> {
                    return x + y;
                });
        System.out.println("所有人总年龄为:" +  reduce); //140
    }
}

案例2:找出最大年龄

public class TestLambda3 {
    public static void main(String[] args) {
        Integer reduce = Stream.of(
                new Person("李隆基", 40),
                new Person("杨玉环", 20),
                new Person("李白", 45),
                new Person("高力士", 35))
                .map(p -> p.getAge())
                .reduce(0,(x,y) -> x > y?x : y);
        System.out.println("最大年龄是:" + reduce); //45
    }
}

mapToInt

如果需要将Stream<Integer>中的Integer类型数据转成int类型,可以使用此方法。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1,2,3,4,5);
        //返回的IntStream流,内部操作的是int类型数据,就可以节省内存,减少自动装箱和拆箱
        IntStream intStream = stream.mapToInt(Integer::intValue);
    }
}

concat

如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("黑格尔");
        Stream<String> stream2 = Stream.of("第欧根尼");
        Stream<String> newStream = Stream.concat(stream1, stream2);
        newStream.forEach(System.out::println);
    }
}

5、收集Stream流中的结果

将流中的数据收集到集合中

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa","bb","cc","dd","ee");
        //最终结果收集到集合中
        List<String> collect = stream.limit(3).collect(Collectors.toList());
        System.out.println(collect);  //[aa, bb, cc]
    }
}

将流中的数据收集到数组中

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("aa","bb","cc","dd","ee");
        //最终结果收集到数组中
        String[] strings = stream.toArray(String[]::new);
    }
}

对流中数据进行聚合计算

当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作。比如获取最大值、获取最小值、求总和、平均值、统计数量。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("李白",45),
                new Person("高力士",35));
        //获取最大年龄
        Optional<Person> collectMax = streamPerson.collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()));
        System.out.println(collectMax.get());
        //获取最小年龄
        Optional<Person> collectMin = streamPerson.collect(Collectors.minBy((s1, s2) -> s1.getAge() - s2.getAge()));
        System.out.println(collectMin.get());
        //获取总年龄
        Integer collectSum = streamPerson.collect(Collectors.summingInt(s -> s.getAge()));
        System.out.println(collectSum);
        //获取年龄平均值
        Double collectAvg = streamPerson.collect(Collectors.averagingInt(s -> s.getAge()));
        System.out.println(collectAvg);
    }
}

对流中数据进行分组

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("安禄山",20),
                new Person("李白",45),
                new Person("白居易",45),
                new Person("高力士",35));
        //根据年龄进行分组
        Map<Integer, List<Person>> map = streamPerson.collect(Collectors.groupingBy((p) -> p.getAge()));
        map.forEach((k,v) -> {
            System.out.println(k + ":" + v);
        });
    }
}

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("安禄山",20),
                new Person("李白",45),
                new Person("白居易",45),
                new Person("高力士",35));
        Map<String, List<Person>> map = streamPerson.collect(Collectors.groupingBy((p) -> {
            if(p.getAge() > 20){
                return "成年";
            }else{
                return "未成年";
            }
        }));
        map.forEach((k,v) -> {
            System.out.println(k + ":" + v);
        });
    }
}

对流中数据进行分区

Collectors.partitioningBy会根据值是否为true,把集合分隔为两个列表,一个true列表,一个false列表。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("安禄山",20),
                new Person("李白",40));
        //年龄大于30的分为true区
        Map<Boolean, List<Person>> map = streamPerson.collect(Collectors.partitioningBy(p -> p.getAge() > 30));
        map.forEach((k,v) -> {
            System.out.println(k + ":" + v);
        });
    }
}

对流中数据进行拼接

Collectors.joining会根据指定的连接符,将所有元素连接成一个字符串。

public class TestLambda3 {
    public static void main(String[] args) {
        Stream<Person> streamPerson = Stream.of(
                new Person("李隆基",40),
                new Person("杨玉环",20),
                new Person("安禄山",20),
                new Person("李白",40));
        //根据一个字符串进行拼接
        String collect = streamPerson.map(Person::getName).collect(Collectors.joining("__"));
        System.out.println(collect); //李隆基__杨玉环__安禄山__李白

        //根据三个字符串进行拼接
        String collect = streamPerson.map(Person::getName).collect(Collectors.joining("__","@","。"));
        System.out.println(collect); //@李隆基__杨玉环__安禄山__李白。
    }
}

6、并行的Stream流

注意:目前我们使用的Stream是串行的,就是在一个线程上执行的 。

获取并行Stream流的两种方式

第一种:直接获取并行的Stream流
//第一种:直接获取并行的Stream流
List<String> list = new ArrayList<>();
Stream<String> stream01 = list.parallelStream();

第二种:将串行流转成并行流
//第二种:将串行流转成并行流
Stream<String> stream02 = list.stream().parallel();

串行与并行Stream代码对比

public class TestLambda3 {
    public static void main(String[] args) {
        //串行
        Stream<Integer> stream = Stream.of(5, 8, 1, 2, 7, 9);
        stream.filter(i -> {
            System.out.println(Thread.currentThread() + "  " + i);
            return i > 2;
        }).count();
    }
}

public class TestLambda3 {
    public static void main(String[] args) {
        //并行
        Stream<Integer> stream = Stream.of(5, 8, 1, 2, 7, 9);
        stream.parallel().filter(i -> {
            System.out.println(Thread.currentThread() + "  " + i);
            return i > 2;
        }).count();
    }
}

parallelStream线程安全问题

先看一个线程不安全的实例:

public class TestLambda3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        IntStream.rangeClosed(1,1000).parallel().forEach(i -> {
            list.add(i);
        });
        System.out.println(list.size()); //928
    }
}

首先总共1000条数据,最后打印的集合size也应该是1000啊,结果打印出928,说明在并行的情况下,使用ArrayList是线程是不安全的。

解决方案一:同步代码块
public class TestLambda3 {
    public static void main(String[] args) {
        Object obj = new Object();
        List<Integer> list = new ArrayList<>();
        IntStream.rangeClosed(1,1000).parallel().forEach(i -> {
            synchronized (obj){ //加入同步代码块
                list.add(i);
            }
        });
        System.out.println(list.size()); //1000
    }
}

解决方案二:使用线程安全的集合
public class TestLambda3 {
    public static void main(String[] args) {
        List<Integer> list = new Vector<>();
        IntStream.rangeClosed(1,1000).parallel().forEach(i -> {
            list.add(i);
        });
        System.out.println(list.size()); //1000
    }
}

解决方案三:使用Collections.synchronizedList方法将集合转为线程安全的
public class TestLambda3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        //将ArrayList转为线程安全的集合
        List<Integer> integers = Collections.synchronizedList(list);
        IntStream.rangeClosed(1,1000).parallel().forEach(i -> {
            integers.add(i);
        });
        System.out.println(list.size()); //1000
    }
}

解决方案四:调用Stream流的collect/toArray
public class TestLambda3 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        List<Integer> collect = IntStream.rangeClosed(1, 1000).parallel().boxed().collect(Collectors.toList());
        System.out.println(collect.size()); //1000
    }
}

parallelStream底层

Fork/join框架介绍

parallelStream使用的是Fork/join框架。Fork/join框架自JDK1.7引入。Fork/join框架可以将一个大任务拆分为很多小任务来异步执行。

Fork/join框架主要包含三个模块:

1、线程池:ForkJoinPool

2、任务对象:ForkJoinTask

3、执行任务的线程:ForkJoinWorkerThread

Fork/join原理-分治法

ForkJoinPool主要用来使用分治法来解决问题。典型的应用比如快速排序算法,ForkJoinPool需要使用相对少的线程来处理大量的任务。比如要对1000万个数据进行排序,那么会将这个任务分割成两个500万的排序任务和一个针对这两组500万数据的合并任务。以此类推,对于500万的数据也会做出同样的分割处理,到最后会设置一个阈值来规定当数据规模到多少时,停止这样的分割处理。比如,当元素的数量小于10时,会停止分割,转而使用插入排序对他们进行排序。那么到最后,所有的任务加起来会有大概2000000+个。问题的关键在于,对于一个任务而言,只有当它所有的子任务完成之后,它才能够被执行。

Fork/join原理-工作窃取算法

Fork/join最核心的地方就是利用了现代硬件设备多核,在一个操作时候会有空闲的CPU,那么如何利用好这个空闲的CPU就成了提高性能的关键,在这里我们要提到工作窃取算法就是整个Fork/join框架的核心理念Fork/join工作窃取算法是指某个线程从其它队列窃取任务来执行

有的线程任务执行的比较快,就没事干了,这时就会窃取其它线程的任务来执行。

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

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

相关文章

【JAVA核心知识】分布式事务框架Seata

Seata 基本信息 GitHub&#xff1a;https://github.com/seata/seatastars: 20.6k 最新版本&#xff1a; v1.6.1 Dec 22, 2022 官方文档&#xff1a;http://seata.io/zh-cn/index.html 注意 官方仅仅支持同步调用。 官方在FAQ中表示对于异步框架需要自行支持。 具体的扩展思…

学习Go语言Web框架Gee总结--上下文Context(二)

学习Go语言Web框架Gee总结--上下文Context context/go.modcontext/main.gocontext/gee/context.gocontext/gee/router.gocontext/gee/gee.go 学习网站来源&#xff1a;Gee 项目目录结构&#xff1a; context/go.mod module examplego 1.21.5require gee v0.0.0 replace gee…

python设计模式:模板方法模式

更多Python学习内容&#xff1a;ipengtao.com 软件设计和编程中&#xff0c;设计模式是一种有助于解决常见问题的强大工具。其中之一是"模板方法模式"&#xff0c;它是一种行为型设计模式&#xff0c;允许你定义一个算法的骨架&#xff0c;但将一些步骤的具体实现延迟…

【elfboard linux开发板】7.i2C工具应用与aht20温湿度寄存器读取

1. I2C工具查看aht20的温湿度寄存器值 1.1 原理图 传感器通过IIC方式进行通信&#xff0c;连接的为IIC1总线&#xff0c;且设备地址为0x38&#xff0c;实际上通过后续iic工具查询&#xff0c;这个设备是挂载在iic-0上 1.2 I2C工具 通过i2c工具可以实现查询i2c总线、以及上面…

第7章 参数估计(重点)

注意&#xff1a;区分正态总体还是非正态总体、总体方差已知还是未知、样本是大样本还是小样本&#xff0c;从而使用对应的Z或者t分布。

面试题理解深层次的数组名

目录 引言 一&#xff1a;一维数组 举例如下 1.铺垫知识 数组名是数组首元素的地址&#xff0c;但是有两个特殊情况 &#xff08;1&#xff09;sizeof(数组名) &#xff08;2&#xff09;&数组名 2.分析讲解上述代码结果 2.字符数组 举例一如下 1.知识铺垫 …

CMake入门教程【基础篇】CMake+Visual Studio2022构建C++项目

文章目录 1.概述2.Visual Studio 2022简介3.安装Visual Studio 20224.安装CMake5.创建CMake项目6. 构建项目 1.概述 CMake和Visual Studio 2022结合 在现代软件开发中&#xff0c;CMake和Visual Studio 2022的结合提供了一个强大的环境&#xff0c;用于构建和管理各种规模的C项…

STM32 学习(二)GPIO

目录 一、GPIO 简介 1.1 GPIO 基本结构 1.2 GPIO 位结构 1.3 GPIO 工作模式 二、GPIO 输出 三、GPIO 输入 1.1 传感器模块 1.2 开关 一、GPIO 简介 GPIO&#xff08;General Purpose Input Output&#xff09;即通用输入输出口。 1.1 GPIO 基本结构 如下图&#xff0…

C++基础:静态变量(保姆级讲解)

1.静态变量定义 在C的&#xff0c;静态变量是一个非常有用的特性&#xff0c;它在程序执行期间只初始化一次&#xff0c;并在程序的整个执行期间都保持其值。 可能这样子说大家无法特别理解&#xff1a;静态变量该怎么定义呢&#xff1f;静态变量的作用是什么&#xff1f;该如…

算法29:不同路径问题(力扣62和63题)--针对算法28进行扩展

题目&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff0…

网络端口(包括TCP端口和UDP端口)的作用、定义、分类,以及在视频监控和流媒体通信中的定义

目 录 一、什么地方会用到网络端口&#xff1f; 二、端口的定义和作用 &#xff08;一&#xff09;TCP协议和UDP协议 &#xff08;二&#xff09;端口的定义 &#xff08;三&#xff09;在TCP/IP体系中&#xff0c;端口(TCP和UDP)的作用 &#xff08;…

Visual Studio 2017 + opencv4.6 + contribute + Cmake(Aruco配置版本)指南

之前配置过一次这个&#xff0c;想起这玩意就难受&#xff0c;贼难配置。由于要用到里面的一个库&#xff0c;不得已再进行配置。看网上的博客是真的难受&#xff0c;这写一块&#xff0c;那里写一块&#xff0c;乱七八糟&#xff0c;配置一顿发现写的都是错的&#xff0c;还得…

leetcode刷题日记:222. Count Complete Tree Nodes(完全二叉树的节点个数)

这一道题&#xff0c;我们可以选择直接进行二叉树的遍历&#xff0c;将所有结点遍历一遍就能得到完全二叉树的结点个数&#xff0c;时间复杂度为O(n)。 代码如下&#xff1a; int countNodes(struct TreeNode* root) {if(rootNULL){return 0;}return countNodes(root->left…

【Linux】socket基础API

目录 1. 创建socket&#xff08;TCP/UDP&#xff0c;客户端服务器&#xff09; 1.1 第一个参数——domain 1.2 第二个参数——type 1.3 第三个参数——protocol 2. 绑定socket地址&#xff08;TCP/UDP&#xff0c;服务器&#xff09; 2.1 字节序及转换函数 2.2 IP地址及…

【数字图像处理技术与应用】2023-2024上图像处理期中-云南农业大学

一、填空题&#xff08;每空2 分&#xff0c;共 30 分&#xff09; 1、图像就是3D 场景在 二维 平面上的影像&#xff0c;根据其存储方式和表现形式&#xff0c;可以将图像分为 模拟 图像和数字图像两大类&#xff1b; 2、在用计算机对数字图像处理中&#xff0c;常用一个 二…

[C#]yolov8-onnx在winform部署手势识别模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 YOLOv8 是一个 SOTA 模型&#xff0c;它建立在以前 YOLO 版本的成功基础上&#xff0c;并引入了新的功能和改进&#xff0c;以进一步提升性能和灵活性。具体创新包括一个新的骨干网络、一个新…

promise.prototype.finally重写和兼容火狐低版本浏览器

一、finally()方法用于指定不管 Promise 对象最后状态如何&#xff0c;都会执行的操作。该方法是 ES2018 引入标准的 let promise new Promise() promise .then(result > {}) .catch(error > {}) .finally(() > {})finally方法的回调函数不接受任何参数;finally方法…

指令、电流、上下斜坡、颤振频率可调型比例放大器

控制不带电反馈的单或双比例电磁铁的比例阀&#xff0c;如比例泵阀、比例插装阀、比例方向阀、比例压力阀、比例流量阀、比例叠加阀等&#xff1b; 常规比例阀控制电流如650mA、700mA、760mA、830mA、950mA、1.6A、2.5A、3A等; 带数显区显示及当前参数现场可调&#xff0c;如…

php合并数组的几种方式 并简述其特点

目前工作中接触到的PHP数组合并方式主要有三种&#xff1a; 1、操作符 2、array_merge() 3、array_merge_recursive() 它们的区别主要体现在对于相同键名&#xff08;数字键名、字符串键名&#xff09;的处理方式&#xff0c; 一 相同字符串键 <?php$arrFirst [&quo…