异步任务——CompletabelFuture

news2024/12/28 5:23:49

本专栏学习内容又是来自尚硅谷周阳老师的视频

有兴趣的小伙伴可以点击视频地址观看

在学习CompletableFuture之前,必须要先了解一下Future

Future

概念

Future接口(FutureTask实现类)定义了操作异步任务执行的一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。

比如主线程在执行多个任务时,其中有一个任务耗时特别久,可以交给子线程执行,主线程继续做自己的事情,过一会再去获取子任务的执行结果。

FutureTask

FutureTaskFuture的一个实现类

引出FutureTask

Future提供了一种异步并行计算的功能,目的是:异步多线程任务执行且返回有结果,三个特点

  • 多线程
  • 有返回
  • 异步任务

说句题外话,小黄一直不了解多线程和异步有什么区别,刚好问了ChatGpt

多线程和异步是两个相关但又不完全相同的概念。

多线程指的是在一个程序中同时运行多个线程,每个线程都是独立执行的。多线程可以使程序具有并发性,提高系统的吞吐量和响应速度。通过多线程,可以将耗时的操作和任务分配给不同的线程来执行,从而提高整体的工作效率。

异步指的是一种编程模型,其中任务的执行不需要等待前一个任务的完成。在异步编程中,可以执行一个任务,而不需要阻塞等待其完成,而是继续执行后续的任务。异步操作通常适用于涉及网络请求、文件读写、数据库查询等可能造成阻塞的操作。通过异步操作,可以充分利用等待时间来执行其他任务,提高程序的效率。

多线程和异步之间存在一定的关系。在实现异步操作时,通常会使用多线程来实现异步任务的执行。通过创建新的线程,可以在后台执行耗时的操作,而不会阻塞主线程的执行。

例如,对于一个网络请求,使用多线程可以在主线程发送请求后,继续执行其他任务,而不需要等待网络请求返回的数据。当网络请求完成后,在新的线程中处理响应数据,从而实现异步操作。

总结起来,多线程是一种实现并发性的机制,而异步是一种编程模型,在实现异步操作时通常会使用多线程来达到异步执行的效果。多线程可以提供资源的并行使用,而异步可以提高程序的运行效率和响应性。

来看一下创建线程的方式

实现Runnable接口可以开启多线程

实现Callable接口可以开启多线程并且有返回值

class MyThread1 implements Runnable {
    @Override
    public void run() {

    }
}

class MyThread2 implements Callable<String> {
    @Override
    public String call() throws Exception {
        return null;
    }
}

但是Thread的构造方法中只有参数为Runnable的方法,无法满足我们的需求

image-20230712161344204

继续往下看,发现RunnableFuture接口继承了Runnable以及Future,那也就是说他具有多线程、异步两个特点

image-20230712161533902

FutureTask实现了RunnableFuture接口,并且他的构造方法中可以传入Callable,那么他就同时具有了多线程、异步、有返回三大特点。

image-20230712161709601

优点

Future配合线程池异步多线程,能显著提高程序的效率

需求

主线程需要执行三个任务,且三个任务耗时分别是500毫秒、300毫秒、300毫秒,如果主线程自己执行的话,那程序至少需要花费11秒的时间,现在使用Future + 线程池来优化

实现

public static void main(String[] args) throws InterruptedException {
    //创建线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(3);
    long startTime = new Date().getTime();
    //创建任务
    FutureTask<String> task1 = new FutureTask<>(() -> {
        Thread.sleep(500);
        return "task1 over";
    });
    threadPool.submit(task1,"t1");

    FutureTask<String> task2 = new FutureTask<>(() -> {
        Thread.sleep(300);
        return "task2 over";
    });
    threadPool.submit(task2,"t2");

    Thread.sleep(300);
    System.out.println("task3 over");
    long endTime = new Date().getTime();
    System.out.println("花费" + (endTime - startTime) + "毫秒");  //花费338毫秒
}

缺点

get方法会阻塞

调用task1.get()会使主线程阻塞,因为get()他会一直等待子线程返回结果在继续运行。

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //创建任务
    FutureTask<String> task1 = new FutureTask<>(() -> {
        Thread.sleep(5000);
        return "task1 over";
    });
    new Thread(task1,"t1").start();
    System.out.println("t1线程结果:" + task1.get());
    System.out.println("主线程执行完毕");
}

isDone方法轮询

对于上述代码,没有一个友好的提示,导致我们不知道程序为何阻塞,FutureTask提供了isDone(),调用该方法,结果为true表示线程执行完毕。

但是这种方法的结果就是需要不停的轮询,大量的消耗了CPU

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //创建任务
    FutureTask<String> task1 = new FutureTask<>(() -> {
        Thread.sleep(5000);
        return "task1 over";
    });
    new Thread(task1,"t1").start();

    while (true) {
        if (task1.isDone()) {
            System.out.println("t1线程结果:" + task1.get());
            break;
        }else {
            Thread.sleep(500);
            System.out.println("请等待");
        }
    }
    System.out.println("主线程执行完毕");
}

更复杂的任务

对于简单的任务使用Future完全可以解决,下面有几个更为复杂的需求Future不好解决了

  • 多个任务前后可以组合处理

    例如:子线程A计算返回的结果,在子线程B中需要用到

  • 对计算速度选最快

    例如:联机游戏,谁先到终点谁就赢了,那么当A到达终点时,B的线程也需要中断

对此,就引出了CompletableFuture,这就有点像一个名场面东厂管不了的,我西厂来管,东厂管得了的,我西厂更要管,也就是说Future有的功能CompletableFuture都有,Future没有的功能CompletableFuture也有,有点像plus版本。

CompletableFuture

之前介绍了Future,发现他只能解决一些简单的逻辑,并且阻塞的方式和异步编程的设计理念相违背,而轮询的方式会消耗无谓的CPU资源,所有CompletableFuture应运而生。

概念

CompletableFuture提供了一种类似于观察者模式的机制,可以让任务执行完后通知监听的一方。

他是JDK1.8新增的类,实现了FutureCompletionStage接口

Future不用在介绍了,CompletionStage提供了一种处理异步操作结果的机制,可以与回调函数一起使用,来处理 CompletableFuture 的计算结果。

image-20230713151615088

创建方式

我们学习一个新的类的方式,第一步就是看他的构造函数,创建这个类,CompletableFuture虽然有一个空参构造函数,但是官方并不推荐我们使用,一般我们通过4个静态方法来创建。

调用静态方法创建返回值
CompletableFuture runAsync(Runnable runnable)
CompletableFuture runAsync(Runnable runnable,Executor executor)
CompletableFuture supplyAsync(Supplier supplier)
supplyAsync(Supplier supplier, Executor executor)

代码实现

runAsync()

不带有线程池,默认使用ForkJoinPool的线程

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
        System.out.println("进入子线程:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    System.out.println(completableFuture.get()); //null
    System.out.println("主线程结束");
}

supplyAsync

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("进入子线程:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int result = ThreadLocalRandom.current().nextInt(10);
        System.out.println("子线程执行结果:" + result);
        return result;
    });

    System.out.println(completableFuture.get());
    System.out.println("主线程结束");
}

带有线程池的创建就不举例了

通用异步编程

上面还是在演示Future原有的功能,接下来学一下新的功能

通过whenComplete来监听子进程执行完毕,来做一系列操作

通过exceptionally来解决子进程出现异常的情况

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("进入子线程:" + Thread.currentThread().getName());
        System.out.println(Thread.currentThread().isDaemon()); //守护线程
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int result = ThreadLocalRandom.current().nextInt(10);
        System.out.println("子线程执行结果:" + result);
        return result;
    }).whenComplete((v,e) -> {
        if (e == null) {
            System.out.println("计算结果为:" + v);
        }
    }).exceptionally(e -> {
        //处理异常
        e.printStackTrace();
        System.out.println("计算过程出现异常:" + e.getCause() + "\t" + e.getMessage());
        return null;
    });
    System.out.println("主线程结束");
}

//输出
进入子线程:ForkJoinPool.commonPool-worker-9
主线程结束

但是发现控制台输出没有等待结果,主线程就直接结束了,这是因为默认情况下ForkJoinPool里面是守护线程,解决方法有两种

  1. 在主线程结束前等待
  2. 使用自定义的线程池

修改代码

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);

    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        System.out.println("进入子线程:" + Thread.currentThread().getName());
        System.out.println(Thread.currentThread().isDaemon()); //守护线程
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int result = ThreadLocalRandom.current().nextInt(10);
        System.out.println("子线程执行结果:" + result);
        return result;
    },threadPool).whenComplete((v,e) -> {
        if (e == null) {
            System.out.println("计算结果为:" + v);
        }
    }).exceptionally(e -> {
        //处理异常
        e.printStackTrace();
        System.out.println("计算过程出现异常:" + e.getCause() + "\t" + e.getMessage());
        return null;
    });
    System.out.println("主线程结束");
    threadPool.shutdown();
}

//输出
进入子线程:pool-1-thread-1
false
主线程结束
子线程执行结果:7
计算结果为:7

通过控制台输出可以看到,自定义线程池创建的是用户线程,所以即使是主线程执行完毕,程序还是要等待所有用户线程执行完毕才会结束。

链式语法

接下来会用到很多链式语法,这个在Java8很常见,其实就是在写法上更加简洁了

public class CompletableFutureDemo5 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Student student = new Student();
        /*
        以前的写法
        student.setId(1);
        student.setName("张三"));
         */
        student.setId(1).setName("张三");
        System.out.println(student);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
class Student{
    private Integer id;
    private String name;
}

join方法

在介绍join()之前,必须先介绍一下get()

get()是获取异步计算结果,但是在编译期会需要抛出异常

image-20230718150444583

join()也是获取异步计算结果,但是不需要抛出异常

image-20230718150548558

电商比价案例

需求:

需要查询《深入理解JVM虚拟机》这本书在各大电商平台销售的价格,显示结果如下

《深入理解JVM虚拟机》in jd price is 100

普通解决方案

使用同步方式,一步一步来,查一个保存一个价格

此方法的优点是简洁粗暴,缺点是非常的耗时

public class CompletableFutureDemo7 {
    static List<NetMall> malls = Arrays.asList(
            new NetMall("jd"),
            new NetMall("tb"),
            new NetMall("pdd")
    );

    public static List<String> step(List<NetMall> list ,String productName){
        return list.stream()
                .map(netMall -> String.format(productName + " in %s price is %.2f",
                        netMall.getNetMallName(),
                        netMall.calcPrice(productName)))
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        long startTime = new Date().getTime();
        List<String> list = step(malls, "MySQL");
        long endTime = new Date().getTime();
        System.out.println("耗时:" + (endTime - startTime));
        for (String item : list) {
            System.out.println(item);
        }
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class NetMall{
    private String netMallName;

    public double calcPrice(String productName) {
        //模拟请求过程耗时
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }
}

//结果
耗时:3067
MySQL in jd price is 78.86
MySQL in tb price is 78.95
MySQL in pdd price is 78.98

异步解决方案

核心计算方法,使用异步的方式进行,这样大大的节约了时间

public static List<String> byCompletableFuture(List<NetMall> list ,String productName){
    return list.stream()
            .map(netMall -> CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
                netMall.getNetMallName(),
                netMall.calcPrice(productName))))
            .collect(Collectors.toList())
            .stream()
            .map(s -> s.join())
            .collect(Collectors.toList());
}

//结果
耗时:1050
MySQL in jd price is 77.77
MySQL in tb price is 77.18
MySQL in pdd price is 77.32

计算方法

CompletableFuture提供了非常多的计算方法

获取结果和触发计算

方法名作用
public T get()获取结果,会造成当前线程阻塞
public T get(long timeout, TimeUnit unit)获取结果,在指定的时间内获取不到,抛出异常
public T join()获取结果,跟get()用法一致,区别是编译器不需要抛异常
public T getNow(T valueIfAbsent)立刻获取结果,如果结果没出来,使用指定值代替结果
public boolean complete(T value)中断计算,计算过程被中断返回true,并且用指定值代替计算结果
public static void main(String[] args) throws InterruptedException {
    CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "123";
    });
    TimeUnit.SECONDS.sleep(2);
    System.out.println(completableFuture.complete("completeValue") + "\t " + completableFuture.join());//true	 completeValue
}

对计算结果进行处理

方法名作用
public CompletableFuture thenApply()获取计算结果,对其进行处理
public CompletableFuture handle()作用同thenApply,区别在于遇到异常不会组织下一步运行

thenApply()

public class CompletableFutureDemo9 {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("111");
            return 1;
        },threadPool).thenApply(f -> {
            System.out.println("222");
            return f + 1;
        }).thenApply(f -> {
            System.out.println("333");
            return f + 2;
        }).whenComplete((v,e) -> {
            if (e == null) {
                System.out.println("计算结果:" + v);
            }
        }).exceptionally(e -> {
            e.printStackTrace();
            return null;
        });

        System.out.println("主线程去忙其他的了 : " + Thread.currentThread().getName());
        threadPool.shutdown();
    }
}

可以看到程序在抛异常时,就停止了,不会继续往下执行

image-20230719135405676

hanlde()

public static void main(String[] args) throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);

    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("111");
        return 1;
    }, threadPool).handle((f, e) -> {
        int i = 1 / 0;
        System.out.println("222");
        return f + 1;
    }).handle((f, e) -> {
        System.out.println("333");
        return f + 2;
    }).whenComplete((v, e) -> {
        if (e == null) {
            System.out.println("计算结果:" + v);
        }
    }).exceptionally(e -> {
        e.printStackTrace();
        return null;
    });

    System.out.println("主线程去忙其他的了 : " + Thread.currentThread().getName());
    threadPool.shutdown();
}

通过输出可以看出在第一个handle中出现异常,不继续往下执行该handle的方法,但是不影响后续的hanlde方法

image-20230719135510783

对计算结果进行消费

消费,顾名思义就是把这条消息消费掉,后面的人就获取不到这条消息了。

方法作用
public CompletableFuture thenRun(Runnable action)任务A执行完执行B,并且B不需要A的结果
public CompletableFuture thenAccept(Consumer<? super T> action)任务A执行完执行B,B需要A的结果,但是任务B没有返回值
public CompletableFuture thenApply任务A执行完执行B,B需要A的结果,同事任务B有返回值

如下所示,thenAccept()方法,在获取结果时为null

public static void main(String[] args) throws InterruptedException {
    System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join()); //null
    System.out.println(CompletableFuture.supplyAsync(() -> "resultB").thenAccept(y -> {System.out.println(y);}).join()); //resultB  null
    System.out.println(CompletableFuture.supplyAsync(() -> "resultC").thenApply(y -> y + "resultD").join()); //resultCresultD
}

对运行线程池进行选择

不使用线程池,默认走的是ForkJoinPool

image-20230719155843600

使用线程池,走的全都是自定义线程池

image-20230719160752242

使用线程池,中间调用了thenRunAsync方法,那么之后的方法都会使用ForkJoinPool

image-20230719160832347

源码

thenRun()thenRunAsync()区别在于一个传参使用了默认的线程池

image-20230719161125302

image-20230719161136157

对计算速度进行选用

调用applyToEither()方法,他会将两个异步任务先完成的值返回

public class CompletableFutureDemo12 {
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
            return "future1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {throw new RuntimeException(e);}
            return "future2";
        });

        CompletableFuture<String> result = future1.applyToEither(future2, s ->  s + " is win");
        System.out.println(result.join()); //future1 is win
    }
}

对计算结果进行合并

thenCombine()可以将两个计算结果合并

public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
    CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
        try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
        return 10;
    });

    CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
        try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {throw new RuntimeException(e);}
        return 20;
    });

    CompletableFuture<Integer> result = future1.thenCombine(future2, (x,y) -> {
        System.out.println("计算结果合并");
        return x + y;
    });
    System.out.println(result.join()); //30
}

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

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

相关文章

编写测试用例的方法,这个是真的很好用

大家测试过程中经常用的等价类划分、边界值分析、场景法等&#xff0c;并不能覆盖所有的需求&#xff0c;我们之前讲过很少用到的因果图法&#xff0c;下面就来讲另一种不经常用到但又非常重要的测试用例编写方法——测试大纲法。 测试大纲法适用于有多个窗口&#xff0c;每个…

Mysql下载详细步骤

一、下载mysql 打开地址&#xff1a;MySQL :: Download MySQL Community Server 这里我下载的是红框标注的。 直接点击No thanks,just start my download.解压后文件看自己需求放置。 红框圈住的文件都是后面自己添加的。 my文件中需要添加的文本内容。 [mysqld] #设置3306端口…

【conan】本地编译三方库,上传conan服务器

1.6 conan 远程已经编译好的库 conan中文博客&#xff1a; 三方库资源&#xff1a; github conan-io 本地查询 conan search Existing package recipes:b2/4.9.6 boost/1.71.0nolovr/stable bzip2/1.0.8 ceres-solver/2.0.0nolovr/stable eigen/3.3.7nolovr/stable eigen_c…

pdf转换成word怎么转换?简单快捷方法分享

pdf转换成word怎么转换&#xff1f;需要将PDF文档转换为Word文档&#xff0c;以便更好地编辑和修改文本内容。比如&#xff0c;当你需要对一份PDF文档中的内容进行修改、编辑或者格式化时&#xff0c;你可以先将其转换为Word文档&#xff0c;再进行修改。这样可以更快速、更便捷…

【数据结构与算法】哈夫曼编码(最优二叉树实现

哈夫曼编码 等长编码&#xff1a;占的位置一样 变长编码&#xff08;不等长编码&#xff09;&#xff1a;经常使用的编码比较短&#xff0c;不常用的比较短 最优&#xff1a;总长度最短 最优的要求&#xff1a;占用空间尽可能短&#xff0c;不占用多余空间&#xff0c;且不…

4.BIO多线程即时通信

highlight: arduino-light 基于BIO模式下的即时通信&#xff0c;我们需要解决客户端到客户端的通信&#xff0c;也就是需要实现客户端与客户端的端口消息转发逻辑。 功能清单 1.客户端登陆功能 可以启动客户端进行登录&#xff0c;客户端登陆只需要输入用户名和服务端ip地址即可…

JVM理论(五)执行引擎--解释器/JIT编译器

概述 首先执行引擎是java虚拟机核心的组成部分之一;而JVM的主要任务是装载字节码到内存,但不能够直接运行在操作系统之上.因为字节码指令并非等价于本地机器指令,它仅仅包含能够被JVM所识别的指令、符号表、以及其他信息;而此时执行引擎就华丽登场,它的任务就是将字节码指令解…

欧姆龙PLC联网

一、设备信息确认 左上角的为PLC型号,如图该PLC型号为CP1H,不同型号的欧姆龙PLC通讯方面有什么差别呢? 通讯能力和方式不同: 有些型号PLC自带网口,有些则需要扩展(上图中右侧的两个红框内为后扩展的通讯口,扩展模块可以随意组合双网口,双232串口,双485串口都可以)…

D354周赛复盘:特殊元素平方和+数组最大美丽值(滑动窗口)+合法分割最小下标

文章目录 6889.特殊元素平方和思路完整版取模注意&#xff1a;不能对0取余/取模解答错误&#xff1a;本题的数组最后一个下标是nums[nums.size()] 6929.数组的最大美丽值&#xff08;排序滑动窗口&#xff09;思路1&#xff1a;排序滑动窗口注意点 6927. 合法分割的最小下标&am…

My_window类(带有next和quit按钮)

运行代码&#xff1a; //My_window类&#xff08;带有next和quit按钮&#xff09; #include"std_lib_facilities.h" #include"GUI/Simple_window.h" #include"GUI/GUI.h" #include"GUI/Graph.h" #include"GUI/Point.h"//--…

为什么项目可见性难以实现?该如何提高?

在项目和专业服务管理中&#xff0c;失败有时难以避免。沟通不足和需求定义不明确被认为是造成失败的最大原因&#xff0c;这意味着项目可见性和信息流动至关重要。 什么是项目可见性&#xff1f; 项目可见性是组织项目相关信息的方式&#xff0c;以便所有团队成员、项目经理…

火狐安卓版支持油猴了!后面将支持更多扩展插件

日前火狐浏览器每夜构建版的安卓版已经带来了更多扩展程序支持&#xff0c;这其中就包括大名鼎鼎的油猴扩展程序。本次火狐浏览器每夜构建版更新新增五款扩展程序支持&#xff0c;并且按照谋智基金会说法还会支持更多的扩展程序。 下载地址&#xff1a;https://ftp.mozilla.org…

力扣 406. 根据身高重建队列

题目来源&#xff1a;https://leetcode.cn/problems/queue-reconstruction-by-height/description/ C题解1&#xff1a;分别对h和k两个维度进行考虑&#xff0c;我这里是优先考虑k值&#xff0c;k值相同的时候h小的排前面。然后再一一遍历&#xff0c;对于people[i]&#xff0c…

曲师大2023大一新生排位赛-D.Factor题解

D.Factor 题目描述 你有一个集合 &#xff0c;和具有 个正整数的数组 . 最初&#xff0c;集合 为空&#xff08;不包含任一元素&#xff09;。你将按照以下方式填充集合 : 以此枚举数组 a 中的每个元素。对于数组中的第 i 个元素 &#xff0c;生成 ​ 的因子集合 ​。如果…

uniapp引入echarts

作为前端在开发需求的时候经常会遇到将数据展示为图表的需求&#xff0c;之前一直用的HBuilder的图表插件uCharts&#xff0c;使用方法可以参考我的另一篇博客&#xff1a;uniapp 中使用图表&#xff08;秋云uCharts图表组件&#xff09; 但是最近发现uCharts很多功能都需要付…

国密算法概述、及算法的集成应用(sm2、sm3、sm4)

国密算法概述、及算法的集成应用&#xff08;sm2、sm3、sm4&#xff09; 一、概述二、分类概述3.1、SM1对称密码3.2、SM2椭圆曲线公钥密码算法3.3、SM3杂凑算法3.4、SM4对称算法3.5、SM7对称密码3.6、SM9标识密码算法3.7、ZUC祖冲之算法 三、集成SM2加解密四、集成SM3加密、验签…

系统学习Linux-Rsync远程数据同步服务(三)

一、概述 rsync是linux 下一个远程数据同步工具 他可通过LAN/WAN快速同步多台主机间的文件和目录&#xff0c;并适当利用rsync 算法减少数据的传输 会对比两个文件的不同部分&#xff0c;传输差异部分&#xff0c;因此传输速度相当快 rsync可拷贝、显示目录属性&#xff0c…

将TXT转化为PDF的方法有哪些,分享四个给大家!

将TXT文本文件转换为PDF是一项常见的需求&#xff0c;特别是在需要共享文档时。在本文中&#xff0c;我们将分享四种方法&#xff0c;让您能够轻松地将TXT文件转换为PDF格式。 方法一&#xff1a;使用记灵在线工具 记灵在线工具是一个方便易用的在线文档转换工具&#xff0c;…

日撸java三百行day77-79

文章目录 说明GUI1. GUI 总体布局2. GUI 代码理解2.1 对话框相关控件2.1.1 ApplicationShowdown.java&#xff08;关闭应用程序&#xff09;2.1.2 DialogCloser.java&#xff08;关闭对话框&#xff09;2.1.3 ErrorDialog.java&#xff08;显示错误信息&#xff09;2.1.4 HelpD…

C++基础算法离散化及区间合并篇

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C算法 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要讲解了双指针&#xff0c;位运算&#xff0c;离散化以及区间合并。…