JDK8新特性之Stream流

news2024/11/25 10:33:08

目录

集合处理数据的弊端

Stream流的获取方式

对于Collection的实现类

对于Map

对于数组

Stream常用方法介绍

count

forEach

filter

limit

skip

map

sorted

distinct

match

find

max和min

reduce

mapToInt

concat

Stream结果收集

结果收集到集合中

结果收集到数组中

对流中的数据做聚合计算

对流中数据做分组操作

对流中的数据做分区操作

对流中的数据做拼接

并行的Stream流

串行的Stream流

并行流

线程安全问题


集合处理数据的弊端

当我们在需要对集合中的元素进行操作的时候,除了必需的添加,删除,获取外,最典型的操作就是 集合遍历

看这个例子:

public class StreamTest1 {

    @Test
    public void test() {

        // 定义一个List集合
        List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");
        
        // 1.获取所有 姓张的信息
        List<String> list1 = new ArrayList<>();
        for (String s : list) {
            if (s.startsWith("张")) {
                list1.add(s);
            }
        }
        
        // 2.获取名称长度为3的用户
        List<String> list2 = new ArrayList<>();
        for (String s : list1) {
            if (s.length() == 3) {
                list2.add(s);
            }
        }
        // 3. 输出满足条件的所有用户的信息
        for (String s : list2) {
            System.out.println(s);
        }
        
    }
}

上面的代码针对与我们不同的需求总是一次次的循环循环循环.这时我们希望有更加高效的处理方式,这 时我们就可以通过JDK8中提供的Stream API来解决这个问题了。

Stream更加优雅的解决方案:

    @Test
    public void test2(){

        List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");

        list.stream()
                .filter(s->s.startsWith("张"))
                .filter(s->s.length()==3)
                .forEach(System.out::println);

    }

看上面的代码够简短吧。获取流,过滤张,过滤长度,逐一打印。代码相比于上面的案例更加的简 洁直观,接下来我们就来学习学习stream流。

Stream流的获取方式

对于Collection的实现类

首先,java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实 现都可以通过steam方法来获取Stream流。

public class getStreamTest {

    @Test
    public void test1(){

        ArrayList<Object> objects = new ArrayList<>();
        objects.stream();

        HashSet<Object> objects1 = new HashSet<>();
        objects1.stream();
        

        Vector<Object> objects2 = new Vector<>();
        objects2.stream();
    }
}

对于Map

Map接口别没有实现Collection接口,那这时怎么办呢?这时我们可以根据Map的 entrySet() 方法获取对应的key value的集合。

    @Test
    public void test2(){

        HashMap<String, String> map = new HashMap<>();
        map.entrySet().stream();
        
    }

初学者可能不太好理解这个entrySet方法。这样想,map本来是存放一组组键值对的一个容器嘛,那我们要对容器里面的键值对进行遍历,但是遍历不了哇,要么就只能遍历键,要么就只能遍历值,无法完成我们的需求,所以有了entrySet方法,用来将map容器里面的这些键值对一组组的分好,这么一看,是不是这个容器就类似Collection了,这样我们每次遍历的时候每一项都是一个键值对映射项。看下面这个例子:

对于数组

在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所 有Stream接口中提供了静态方法of。

    @Test
    public void test3(){

        String[] a={"aa","bb","cc"};
        Stream<String> a1 = Stream.of(a);
        a1.forEach(System.out::println);

        Integer[] b={1,23,8,5};
        Stream<Integer> b1 = Stream.of(b);
        b1.forEach(System.out::println);

        //基本数据类型不行
        int[] c={1,5,3,6};
        Stream<int[]> c1 = Stream.of(c);
        c1.forEach(System.out::println);
    }

但是注意:基本数据类型的数组不行,必须要是包装数据类型数组。

Stream常用方法介绍

Stream流的操作很丰富,这里只介绍一些常用的API。这些方法可以被分成两种:

终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用,常用的有count方法和forEach方法。

非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。除了终结方法以外的都是非终结方法。

Stream注意事项(重要):

  • Stream只能操作一次
  • Stream方法返回的是新的流
  • Stream不调用终结方法,中间的操作不会执行

count

该方法返回一个long值,表示元素的个数

public static void main(String[] args) {
    long count = Stream.of("a1", "a2", "a3").count();
    System.out.println(count);
}

forEach

该方法接收一个Consumer函数式接口,会将元素遍历交给函数处理

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3").forEach(System.out::println);;
}

filter

filter方法的作用是用来过滤数据的。返回符合条件的数据。该接口接收一个Predicate函数式接口参数作为筛选条件

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3","bb","cc","aa","dd")
        .filter((s)->s.contains("a"))
        .forEach(System.out::println);
}

limit

参数是一个long类型的数值,对该集合的前n个元素进行截取

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3","bb","cc","aa","dd")
        .limit(3)
        .forEach(System.out::println);
}

skip

参数是一个long类型的数值,对该集合的前n个元素进行跳过,和limit恰好相反

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3","bb","cc","aa","dd")
        .skip(3)
        .forEach(System.out::println);
}

map

该接口需要一个Function函数式接口参数,然后对元素进行遍历操作

    @Test
    public void test4(){

        String[] str= {"cc2","2cl2","3vv"};
        List<Integer> collect = Stream.of(str).map(String::length).collect(Collectors.toList());
        System.out.println(collect);
        
    }

sorted

对数据进行排序,默认是可以自然排序,也可以自己实现排序规则

    @Test
    public void test5(){

        Integer[] a={1,8,2,6,58,6};

        Stream.of(a)
              //.sorted()  //默认为自然排序
                .sorted((i,j)->j-i)//自定义规则,这里我们是逆序
                .forEach(System.out::println);
    }

distinct

对数据进行去重操作。

Stream流中的distinct方法对于基本数据类型是可以直接出重的,但是对于自定义类型,我们是需要重写hashCode和equals方法来定义规则。

    @Test
    public void test6(){

        Integer[] a={1,8,2,6,58,6};

        Stream.of(a)
              //.sorted()  //默认为自然排序
                .sorted((i,j)->j-i)//自定义规则,这里我们是逆序
                .distinct()
                .forEach(System.out::println);
    }

match

如果需要判断数据是否匹配指定的条件,可以使用match相关的方法

boolean anyMatch(Predicate predicate); // 元素是否有任意一个满足条件 boolean allMatch(Predicate predicate); // 元素是否都满足条件

boolean noneMatch(Predicate predicate); // 元素是否都不满足条件

public static void main(String[] args) {
    boolean b = Stream.of("1", "3", "3", "4", "5", "1", "7")
        .map(Integer::parseInt)
      //.allMatch(s -> s > 0)
      //.anyMatch(s -> s >4)
        .noneMatch(s -> s > 4);
    System.out.println(b);
}

注意match是一个终结方法

find

如果我们需要找到某些数据,可以使用find方法来实现

Optional findFirst();

Optional findAny();

public static void main(String[] args) {
    Optional<String> first = Stream.of("1", "3", "3", "4", "5", "1","7").findFirst();
    System.out.println(first.get());
    Optional<String> any = Stream.of("1", "3", "3", "4", "5", "1","7").findAny();
    System.out.println(any.get());
}

max和min

如果我们想要获取最大值和最小值,那么可以使用max和min方法

Optional min(Comparator comparator);

Optional max(Comparator comparator);

public static void main(String[] args) {
    Optional<Integer> max = Stream.of("1", "3", "3", "4", "5", "1", "7")
        .map(Integer::parseInt)
        .max((o1,o2)->o1-o2);
    System.out.println(max.get());

    Optional<Integer> min = Stream.of("1", "3", "3", "4", "5", "1", "7")
        .map(Integer::parseInt)
        .min((o1,o2)->o1-o2);
    System.out.println(min.get());
}

reduce

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

public static void main(String[] args) {
    Integer sum = Stream.of(4, 5, 3, 9)
        // identity默认值
        // 第一次的时候会将默认值赋值给x
        // 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素
        .reduce(0, (x, y) -> {
            System.out.println("x="+x+",y="+y);
            return x + y;
        });
    System.out.println(sum);

    // 获取 最大值
    Integer max = Stream.of(4, 5, 3, 9)
        .reduce(0, (x, y) -> {
            return x > y ? x : y;
        });
    System.out.println(max);
}

mapToInt

如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现

public static void main(String[] args) {
        
        // Integer占用的内存比int多很多,在Stream流操作中会自动装修和拆箱操作
        Integer arr[] = {1, 2, 3, 5, 6, 8};
        Stream.of(arr)
                .filter(i -> i > 0)
                .forEach(System.out::println);
        
        // 为了提高程序代码的效率,我们可以先将流中Integer数据转换为int数据,然后再操作
        IntStream intStream = Stream.of(arr)
                .mapToInt(Integer::intValue);
        intStream.filter(i -> i > 3)
                .forEach(System.out::println);
    }

concat

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

public static void main(String[] args) {
    Stream<String> stream1 = Stream.of("a","b","c");
    Stream<String> stream2 = Stream.of("x", "y", "z");

    // 通过concat方法将两个流合并为一个新的流
    Stream.concat(stream1,stream2).forEach(System.out::println);
}

Stream结果收集

结果收集到集合中

    @Test
    public void test7(){
        List<String> list = Stream.of("a", "b", "c")
                .collect(Collectors.toList());
        System.out.println(list);

        Set<String> set = Stream.of("a", "b", "c")
                .collect(Collectors.toSet());
        System.out.println(set);

        ArrayList<String> arrayList = Stream.of("a", "b", "c")
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(arrayList);

        HashSet<String> hashSet = Stream.of("a", "b", "c")
                .collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
    }

结果收集到数组中

Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的 类型,那么可以使用另一个重载的toArray(IntFunction f)方法

    @Test
    public void test8(){

        Object[] objects = Stream.of("a", "b", "c")
                .toArray();
        System.out.println(objects);

        String[] strings = Stream.of("a", "b", "c")
                .toArray(String[]::new);
        System.out.println(strings);
    }

对流中的数据做聚合计算

    @Test
    public void test9() {
        
        // 获取年龄的最大值
        Optional<Person> maxAge = Stream.of(
                new Person("张三", 18)
                , new Person("李四", 22)
                , new Person("张三", 13)
                , new Person("王五", 15)
                , new Person("张三", 19)
        ).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最大年龄:" + maxAge.get());
        
        // 获取年龄的最小值
        Optional<Person> minAge = Stream.of(
                new Person("张三", 18)
                , new Person("李四", 22)
                , new Person("张三", 13)
                , new Person("王五", 15)
                , new Person("张三", 19)
        ).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
        System.out.println("最新年龄:" + minAge.get());
        
        // 求所有人的年龄之和
        Integer sumAge = Stream.of(
                new Person("张三", 18)
                , new Person("李四", 22)
                , new Person("张三", 13)
                , new Person("王五", 15)
                , new Person("张三", 19)
        ).collect(Collectors.summingInt(Person::getAge));
        System.out.println("年龄总和:" + sumAge);
        
        // 年龄的平均值
        Double avgAge = Stream.of(
                new Person("张三", 18)
                , new Person("李四", 22)
                , new Person("张三", 13)
                , new Person("王五", 15)
                , new Person("张三", 19)
        ).collect(Collectors.averagingInt(Person::getAge));
        System.out.println("年龄的平均值:" + avgAge);
        
        // 统计数量
        Long count = Stream.of(
                new Person("张三", 18)
                , new Person("李四", 22)
                , new Person("张三", 13)
                , new Person("王五", 15)
                , new Person("张三", 19)
        ).filter(p -> p.getAge() > 18)
                .collect(Collectors.counting());
        System.out.println("满足条件的记录数:" + count);
    }

对流中数据做分组操作

当我们使用Stream流处理数据后,可以根据某个属性将数据分组

    @Test
    public void test10() {

        // 根据账号对数据进行分组
        Map<String, List<Person>> map1 = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).collect(Collectors.groupingBy(Person::getName));
        map1.forEach((k, v) -> System.out.println("k=" + k + "\t" + "v=" + v));

        // 根据年龄分组 如果大于等于18 成年否则未成年
        Map<String, List<Person>> map2 = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
        map2.forEach((k, v) -> System.out.println("k=" + k + "\t" + "v=" + v));
    }

对流中的数据做分区操作

Collectors.partitioningBy会根据值是否为true,把集合中的数据分割为两个列表,一个true列表,一个 false列表

    @Test
    public void test11() {
        Map<Boolean, List<Person>> map = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).collect(Collectors.partitioningBy(p -> p.getAge() > 18));
        map.forEach((k, v) -> System.out.println(k + "\t" + v));
    }

对流中的数据做拼接

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

    @Test
    public void test12() {

        String s1 = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).map(Person::getName)
                .collect(Collectors.joining());
        // 张三李四张三李四张三
        System.out.println(s1);

        String s2 = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).map(Person::getName)
                .collect(Collectors.joining("_"));
        // 张三_李四_张三_李四_张三
        System.out.println(s2);

        String s3 = Stream.of(
                new Person("张三", 18, 175)
                , new Person("李四", 22, 177)
                , new Person("张三", 14, 165)
                , new Person("李四", 15, 166)
                , new Person("张三", 19, 182)
        ).map(Person::getName)
                .collect(Collectors.joining("_", "###", "$$$"));
        // ###张三_李四_张三_李四_张三$$$
        System.out.println(s3);
    }

并行的Stream流

串行的Stream流

我们前面使用的Stream流都是串行,也就是在一个线程上面执行。

@Test
public void test01(){
    Stream.of(5,6,8,3,1,6)
        .filter(s->{
            System.out.println(Thread.currentThread() + "" + s);
        return s > 3;
    }).count();
}

Thread[main,5,main]5

Thread[main,5,main]6

Thread[main,5,main]8

Thread[main,5,main]3

Thread[main,5,main]1

Thread[main,5,main]6

并行流

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。

我们可以通过两种方式来获取并行流:

  • 通过List接口中的parallelStream方法来获取
  • 通过已有的串行流转换为并行流(parallel)

@Test
public void test02(){
    List<Integer> list = new ArrayList<>();
    // 通过List 接口 直接获取并行流
    Stream<Integer> integerStream = list.parallelStream();

    // 将已有的串行流转换为并行流
    Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
}

测试:

@Test
public void test03(){
    Stream.of(1,4,2,6,1,5,9)
        .parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
        .filter(s->{
            System.out.println(Thread.currentThread() + " s=" +s);
            return s > 2;
        }).count();
}

Thread[main,5,main] s=1

Thread[ForkJoinPool.commonPool-worker-2,5,main] s=9 Thread[ForkJoinPool.commonPool-worker-6,5,main] s=6 Thread[ForkJoinPool.commonPool-worker-13,5,main] s=2 Thread[ForkJoinPool.commonPool-worker-9,5,main] s=4 Thread[ForkJoinPool.commonPool-worker-4,5,main] s=5 Thread[ForkJoinPool.commonPool-worker-11,5,main] s=1

线程安全问题

在多线程的处理下,肯定会出现数据安全问题。如下:

    @Test
    public void test9(){

        ArrayList<Integer> arrayList1 = new ArrayList<>();
        for (int i=0;i<1000;i++){
            arrayList1.add(i);
        }

        ArrayList<Integer> arrayList2 = new ArrayList<>();
        arrayList1.parallelStream()
                .forEach(arrayList2::add);
        System.out.println(arrayList2.size());
    }

运行以后结果并不是1000

针对这个问题,我们的解决方案有哪些呢?

  • 加同步锁
  • 使用线程安全的容器
  • 通过Stream中的toArray/collect操作

    //加同步锁
    @Test
    public void test9() {

        Object o = new Object();
        ArrayList<Integer> arrayList1 = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            arrayList1.add(i);
        }

        ArrayList<Integer> arrayList2 = new ArrayList<>();
        arrayList1.parallelStream()
                .forEach((a) -> {
                    synchronized (o) {
                        arrayList2.add(a);
                    }
                });
        System.out.println(arrayList2.size());
    }

    //使用线程安全的容器
    @Test
    public void test10() {

        Vector<Integer> objects = new Vector<>();
        for (int i = 0; i < 1000; i++) {
            objects.add(i);
        }

        Vector<Integer> vector = new Vector<>();
        objects.parallelStream()
                .forEach((a) -> {
                    vector.add(a);
                });
        System.out.println(vector.size());
    }

    //将线程不安全的容器包装为线程安全的容器
    @Test
    public void test11() {
        List<Integer> listNew = new ArrayList<>();

        List<Integer> synchronizedList = Collections.synchronizedList(listNew);

        IntStream.rangeClosed(1, 1000)
                .parallel()
                .forEach(i -> {
                    synchronizedList.add(i);
                });
        System.out.println(synchronizedList.size());
    }

    //通过Stream中的 toArray方法或者 collect方法来操作
    @Test
    public void test12() {

        List<Integer> list = IntStream.rangeClosed(1, 1000)
                .parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(list.size());
    }

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

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

相关文章

分布式定时调度-xxl-job

分布式定时调度-xxl-job 一.定时任务概述 1.定时任务认识 1.1.什么是定时任务 定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行&#xff0c;或者周期性的去执行某个任务&#xff0c;比如&#xff1a;每天晚上24点做数据汇总&#xff0c;定时发送短信等。 …

Android中Adapter的作用

Adapter的介绍 An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making aView for each item in the data set. 一个Adapter是Ada…

关于 SAP Cloud Connector 500 failed to sign the Certificate 的错误消息

有朋友向我询问一个关于 SAP Cloud Connector 的问题&#xff0c;错误消息如下&#xff1a; 500 failed to sign the Cloud Connector Certificate for subaccount XXX. Verify Configuration and proxy settings. See Log And Trace Files and in particular ljs_trace.log fo…

基于Java+SpringBoot+Mybatis+Vue+ElementUi的校园闲置物品交易

项目介绍 我们通过java语言&#xff0c;后端springboot框架&#xff0c;数据库mysql&#xff0c;前端vue技术&#xff0c;开发本系统&#xff0c;校园闲置物品交易网站系统中的功能模块主要是实现管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、商品信息管理、系…

什么是固话号码认证?固话号码认证有用吗?

固话号码认证提供企业号码认证服务&#xff0c;来电场景中展现企业LOGO&#xff0c;展现品牌&#xff0c;可以查看更多企业相关信息&#xff0c;可有效提高接通率&#xff0c;保证品牌企业的身份及商业价值。 那如何实施号码认证服务呢&#xff1f;接下来小编就给大家整理了号…

ICML-2022 | 强化学习论文清单(附链接)

第39届国际机器学习会议&#xff08;International Conference on Machine Learning, ICML 2022&#xff09;于北京时间7月17日至7月23日&#xff0c;在美国马里兰州巴尔的摩市以线上线下结合的方式举办。 本文列举了会议主题与强化学习&#xff08;Reinforcement Learning, R…

20行Python代码,轻轻松松获取各路书本,你还在花钱买着看嘛~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 作为现代青年&#xff0c;我相信应该没几个没看过xiao shuo的吧&#xff0c;嘿嘿~ 一般来说咱们书荒的时候怎么办&#xff1f; 自然是去寻一个网站先找到xiao shuo名字&#xff0c;然后再找度娘…

高压功率放大器原理和应用场合介绍

高压功率放大器是用来对&#xff08;脉冲发生器、函数发生器以及波形发生器等&#xff09;信号进行电压、电流或者功率放大&#xff0c;能够给测试负载提供信号驱动的测量仪器。独立的波形功率放大器分为电压放大器、电流放大器以及电压/电流放大器。另外&#xff0c;还有一类被…

Spring的常用拓展点

文章目录自定义拦截器获取 Spring 容器对象修改 BeanDefinition添加BeanDefinition测试初始化 Bean 前后初始化方法使用PostConstruct 注解实现 InitializingBean 接口BeanFactoryPostProcessor 接口关闭容器前自定义作用域自定义拦截器 spring mvc 拦截器的顶层接口是&#x…

web端常见导航设计

一、导航的定义 导航作为网站或者平台的骨架&#xff0c;是产品设计中不容忽视的一环导航是内容或者功能的定位、导向与通道。 二、导航分类 遵循导航层级结构&#xff0c;包括全局导航和局部导航 全局导航往往指页眉和页脚&#xff0c;存在于网站的大部分页面&#xff0c;便于…

游戏引擎概述-Part1

一、简述自己的学习心路历程 自从业UNITY以来已经有4个月多了&#xff0c;回想起来自己从工作以来就很少写博客了&#xff0c;也算督促一下自己&#xff0c;回想自己从最早的Unity开始&#xff0c;入手C#和编辑器、Unity开发界面&#xff0c;再到自己学一些Unity的小项目…

有效学习,通过PMP考试

但是我们时间有限&#xff0c;如何有效利用这些资料&#xff0c;花最少时间通过考试&#xff0c;是个关键问题。 课程主要的资料包括&#xff1a; PMBOK。官方指定用书&#xff0c;考试知识点来自PMBOK。 汪博士解读PMP考试。考试参考书&#xff0c;比PMBOK解析得更清楚&…

Qt-FFmpeg开发-视频播放(3)

Qt-FFmpeg开发-视频播放【软解码 OpenGL显示RGB图像】 文章目录Qt-FFmpeg开发-视频播放【软解码 OpenGL显示RGB图像】1、概述2、实现效果3、FFmpeg软解码流程4、主要代码4.1 解码代码4.2 OpenGL显示RGB图像代码5、完整源代码更多精彩内容&#x1f449;个人内容分类汇总 &…

Bio-Helix 艾美捷IRIS11预染蛋白Markers基参及相关研究

IRIS11预染色蛋白梯是11种分子量为3至60kDa的预染色蛋白的组合。11种重组蛋白与蓝色发色团共价偶联&#xff0c;而2条70kDa和260kDa的红色带、一条15kDa的绿色带和一条新设计的60kDa的孔雀绿色带作为参考带。IRIS11预拉伸蛋白质阶梯在SDS聚丙烯酰胺凝胶电泳过程中跟踪蛋白质的大…

[附源码]java毕业设计新冠疫苗线上预约系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【测试沉思录】17. 性能测试中的系统资源分析之四:网络

作者&#xff1a;马海琴 编辑&#xff1a;毕小烦 计算机网络&#xff0c;就是通过光缆、电缆、电话线或无线通讯将两台以上的计算机互连起来的集合&#xff0c;包括广域网、城域网、局域网和无线网。 计算机网络是传输信息的媒介。我们常说的千兆网&#xff0c;是指网络带宽为…

openssl中SM2、SM3、SM4使用实例

目录 openssl的版本如下&#xff1a; SM3使用实例 1. SM3的扎凑实例 SM2使用实例 1. 生成SM2密钥对 2.查看SM2密钥对 3.生成自签名证书 4.查看证书详情 5.私钥签名消息 6.证书验证消息签名 SM4 使用实例 openssl的版本如下&#xff1a; SM3使用实例 SM3是中华人民共…

前端框架 Nextjs 实现React SEO优化

目录 一、Nextjs框架创建React项目 二、路由的使用 1、静态路由 2、动态路由 3、Link路由跳转 4、Api路由 5、Api动态路由 三、Nextjs中加载js脚本 四、Nextjs中加载图片 五、Nextjs的公共布局 六、Pages的其他特性 一、Nextjs框架创建React项目 快速入门 | Next.…

YOLOv5~目标检测模型精确度

还是yolo5的基础啊~~ 一些关于目标检测模型的评估指标&#xff1a;IOU、TP&FP&FN&TN、mAP等&#xff0c;并列举了目标检测中的mAP计算。 指标评估(重要的一些定义) IOU 也称重叠度表示计算预测回归框和真实回归框的交并比,计算公式如下: TP&FP&FN&…

2022CTF培训(二)Hook进阶反调试

附件下载链接 Hook进阶 更精准的 Inline Hook 要求 实现防止任务管理器对某进程自身的结束要求不影响任务管理器结束其它进程的功能 Dll 注入程序编写 提权 主要过程如下&#xff1a; 首先,程序需要调用OpenProcessToken函数打开指定的进程令牌&#xff0c;并获取TOKEN…