CompletableFuture的使用(详细)

引入

功能和灵活性:
Future:是Java 5引入的接口,用于表示一个异步操作的未来结果。它提供了基本的异步操作支持,如检查是否完成、等待结果以及获取结果,但在处理结果、异常和组合等方面功能有限。
CompletableFuture:是Java 8引入的类,扩展了Future的功能,提供了更强大和灵活的异步编程支持。它允许更容易地处理异步操作的结果、异常和组合,以及在操作完成时执行回调等。
异步编程支持:
Future:仅用于异步操作的基本管理,如提交任务和获取结果。
CompletableFuture:提供了丰富的操作符、方法和组合功能,可以在异步操作之间进行更复杂的处理,如转换、组合、串联、并行等。
组合和链式操作:
Future:没有提供内置的操作符来对多个异步操作进行链式组合。
CompletableFuture:支持在操作完成后进行链式操作,使多个异步操作可以依次执行,以及在其中一个或多个操作完成后执行其他操作。
异常处理:
Future:异常处理相对有限,通常需要使用try-catch块来捕获操作过程中的异常。
CompletableFuture:具有更强大的异常处理机制,可以使用exceptionally()、handle()等方法来处理操作过程中的异常。
回调执行
Future:不支持在操作完成时执行回调操作。
CompletableFuture:支持使用thenApply()、thenCompose()、thenCombine()等方法来在操作完成后执行回调。
并行执行:
Future:通常不直接支持并行执行。
CompletableFuture:通过fork-join池实现了并行执行操作,允许一次性同时处理多个异步任务。

总之:
Future使用局限性:

  • Future的get方法会导致主线程阻塞
  • 轮询获取结果会消耗cpu资源
  • 多个Future任务不能按照顺序执行
  • Future Api无异常处理

创建

// 无返回值 使用ForkJoinPool线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 无返回值 可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
// 有返回值 使用ForkJoinPool线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 有返回值 可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

获取结果
通过get、join、getNow获取返回值,区别如下:

join:返回结果或者抛出一个unchecked异常(CompletionException),不需要显示捕获异常。
get:返回结果或者一个具体的异常(ExecutionException,
InterruptedException),此方法继承至Future是堵塞的。
getNow:如果当前任务执行完成,返回执行结果,否则返回valueIfAbsent(默认值)。

get:get() 方法会阻塞当前线程,直到异步计算完成并返回结果。如果计算过程中抛出异常,该异常会在调用 get() 方法的线程中重新抛出。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  // 模拟耗时操作  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new IllegalStateException(e);  }  return "Hello, World!";  
});  try {  // 阻塞直到结果可用  String result = future.get();  System.out.println(result);  
} catch (InterruptedException | ExecutionException e) {  e.printStackTrace();  
}

join:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  // 模拟耗时操作并抛出异常  throw new RuntimeException("Simulated Exception");  
});  try {  // 阻塞直到结果可用,但异常将作为未检查的异常抛出  String result = future.join();  System.out.println(result); // 这行代码不会被执行,因为会抛出异常  
} catch (CompletionException e) {  // 处理 CompletionException,它包装了原始异常  e.getCause().printStackTrace(); // 输出: java.lang.RuntimeException: Simulated Exception  
}

getnow:如果计算尚未完成,它将立即返回一个给定的默认值。这不会阻塞调用线程。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  // 模拟耗时操作  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  throw new IllegalStateException(e);  }  return "Hello, World!";  
});  // 尝试获取结果,但由于计算尚未完成,将返回默认值  
String result = future.getNow("Default Value");  
System.out.println(result); // 输出: Default Value(如果计算尚未完成)  // 在稍后某个时间点,当计算完成后,你可以再次调用 get() 来获取结果  
try {  result = future.get();  System.out.println(result); // 输出: Hello, World!  
} catch (InterruptedException | ExecutionException e) {  e.printStackTrace();  
}

handle()

它允许你处理异步操作的结果,无论是正常结果还是异常。这个方法接受两个参数:一个 BiFunction 用于处理正常结果,一个Function 用于处理异常(但是通常我们只使用第一个参数,因为异常处理可以通过 exceptionally 方法来完成
handle 方法的目的是允许你在单个回调中同时处理正常结果和异常,并返回一个新的值(或者一个新的 CompletableFuture)。
返回值:
handle 方法返回一个新的 CompletableFuture,该 CompletableFuture 的结果是由提供的 BiFunction 产生的。
异常处理:
你可以在 handle 方法的 BiFunction 中检查异常参数(Throwable 类型),并据此决定如何处理异常。但是,请注意,handle 本身并不区分正常结果和异常,它只是提供了一个函数来处理两者。
非阻塞:
handle 方法本身是非阻塞的,它立即返回一个新的 CompletableFuture,而不需要等待原始 CompletableFuture 完成。

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  if (Math.random() > 0.5) {  throw new RuntimeException("Random error");  }  return "Success";  });  // 使用 handle 处理结果和异常  CompletableFuture<String> handledFuture = future.handle((result, throwable) -> {  if (throwable != null) {  // 处理异常  return "Error occurred: " + throwable.getMessage();  } else {  // 处理正常结果  return "Handled result: " + result;  }  });  // 获取最终结果  System.out.println(handledFuture.get());  }  
}

exceptionally

当你使用 CompletableFuture 进行异步计算时,如果在执行过程中发生异常,你可以通过调用 exceptionally 方法来指定一个函数,这个函数将在异常发生时被调用,并允许你提供一个替代结果或者进行其他的异常处理。

exceptionally 方法接收一个参数,它是一个函数式接口 Function<Throwable, ? extends U> 的实现,其中 Throwable 是抛出的异常类型,U 是 CompletableFuture 的结果类型或其子类型。这个函数接收异常作为输入,并返回一个值作为替代结果。

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  if (Math.random() > 0.5) {  throw new RuntimeException("Random error");  }  return "Success";  }).exceptionally(throwable -> "Error occurred: " + throwable.getMessage());  // 获取最终结果,无论是正常结果还是异常处理后的结果  System.out.println(future.get());  }  
}

allof()

用于创建一个新的 CompletableFuture,这个新的 CompletableFuture 会在所有给定的 CompletableFuture 实例都完成时完成。这里的“完成”意味着无论是正常完成还是异常完成。allOf() 方法本身并不返回任何结果,它只是等待所有给定的 CompletableFuture 都完成。

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

参数 cfs:一个 CompletableFuture 数组,代表要等待完成的所有任务。 返回值 一个新的
CompletableFuture,该 CompletableFuture 在所有给定的 CompletableFuture
都完成时完成。

假设我们有一组异步任务,每个任务都表示为一个 CompletableFuture。我们想要等待所有任务都完成后再进行下一步操作。这时,我们就可以使用 CompletableFuture.allOf() 方法。

// 创建并启动三个异步任务CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "CompletableFuture");CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Java");// 使用 allOf 等待所有任务完成,但不处理结果CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);// 分别处理每个任务的结果future1.thenAccept(result -> System.out.println("Result of future1: " + result));future2.thenAccept(result -> System.out.println("Result of future2: " + result));future3.thenAccept(result -> System.out.println("Result of future3: " + result));// 等待所有任务完成combinedFuture.get();System.out.println("All tasks have completed.");

thenApply()

CompletableFuture.thenApply() 方法用于在原始 CompletableFuture 完成时,对其结果进行某种转换,并返回一个新的 CompletableFuture,这个新的 CompletableFuture 会包含转换后的结果。如果原始 CompletableFuture 异常完成,那么新的 CompletableFuture 也会异常完成,并携带相同的异常。
thenApply()是线程的后续操作,可以拿到上一次线程执行的返回结果作为本次thenApply()的参数一直传递下去。 并且是有返回结果的。

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  public class CompletableFutureThenApplyExample {  public static void main(String[] args) throws ExecutionException, InterruptedException {  // 创建一个异步任务,计算一个数的平方  CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);  // 使用 thenApply 来对结果进行转换,这里我们将结果乘以2  CompletableFuture<Integer> doubledFuture = future.thenApply(result -> result * 2);  // 等待转换后的任务完成,并打印结果  Integer doubledResult = doubledFuture.get();  System.out.println("The result doubled is: " + doubledResult);  }  
}
 // 模拟 1 + 1 + 1CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 1).thenApply(v -> v + 1).thenApply(v -> v + 1);System.out.println("执行结果:" + future.getNow(-1));
返回结果:3

thenAccept() 和 thenRun()方法

thenAccept() 和 thenRun() 是 CompletableFuture 中两个常用的方法,它们用于在异步操作完成时执行回调函数,但它们的用途和参数有所不同。
thenAccept() 方法接收一个 Consumer 类型的参数,当 CompletableFuture 的计算完成时,Consumer 会被调用,并且会传入 CompletableFuture 的结果。thenAccept() 方法不返回新的 CompletableFuture,因为它不关注结果的进一步处理。
thenRun() 方法接收一个 Runnable 类型的参数,当 CompletableFuture 的计算完成时,Runnable 会被调用。与 thenAccept() 不同,thenRun() 不接收任何参数,它不关心 CompletableFuture 的结果。

import java.util.concurrent.CompletableFuture;  public class CompletableFutureExample {  public static void main(String[] args) throws Exception {  CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!");  future.thenAccept(result -> {  System.out.println("Received result: " + result);  // 注意:这里不能返回一个新的值或者CompletableFuture  });  // 注意:不要在这里添加Thread.sleep等待,因为thenAccept是异步的  // 可以通过其他方式(如等待另一个Future)来确保主线程不立即退出  // 例如,可以通过获取future的结果来阻塞主线程,但这不是thenAccept的目的  // future.get(); // 这会阻塞主线程直到future完成  }  
}
import java.util.concurrent.CompletableFuture;  public class CompletableFutureExample {  public static void main(String[] args) throws Exception {  CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {  // 模拟长时间运行的任务  try {  Thread.sleep(2000);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("Long running task finished!");  });  future.thenRun(() -> {  System.out.println("ThenRun action executed!");  // 这里不会接收任何参数,因为它不关心前面任务的结果  });  // 等待future完成以确保thenRun能执行  future.get(); // 这会阻塞主线程直到future完成  }  
}

thenCompose()

thenCompose()方法用于在原始CompletableFuture完成时,根据其结果启动一个新的CompletableFuture链。这允许你基于原始结果动态地创建新的异步操作。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 1);  
CompletableFuture<String> futureString = future.thenCompose(result ->   CompletableFuture.supplyAsync(() -> "The number is " + result * 2)  
);  
futureString.thenAccept(System.out::println); // Prints: The number is 2

complete():当前阶段异步任务执行完成

complete() 方法是用来手动完成一个 CompletableFuture 实例的,并设置其结果值。当你希望从外部(非异步任务本身)来设置 CompletableFuture 的结果时,这个方法就非常有用了。一旦一个 CompletableFuture 被完成(无论是通过 complete() 还是其他方式),它就不能再被完成第二次,并且任何后续的 complete() 或异常完成调用都会被忽略。

import java.util.concurrent.CompletableFuture;  public class CompletableFutureCompleteDirectExample {  public static void main(String[] args) {  // 创建一个新的CompletableFuture实例  CompletableFuture<String> future = new CompletableFuture<>();  // 直接在主线程中调用complete()方法来完成这个CompletableFuture  future.complete("这是直接设置的结果");  // 获取结果,因为complete()已经被调用,所以这里不会阻塞  String result = future.getNow(null); // getNow接受一个默认值,如果CompletableFuture没有完成则返回这个默认值  System.out.println("结果: " + result); // 输出 "结果: 这是直接设置的结果"  }  
}

链式:

import java.util.concurrent.CompletableFuture;  public class CompletableFutureChainExample {  public static void main(String[] args) throws Exception {  // 假设我们有一个异步操作,它返回一个字符串  CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {  try { Thread.sleep(1000);  } catch (InterruptedException e) {  throw new IllegalStateException(e);  }  return "Initial value";  });  // 使用 thenApply 链式操作,对结果进行转换  CompletableFuture<String> future2 = future1.thenApply(s -> s + " transformed");  // 不返回新的CompletableFuture  future2.thenAccept(System.out::println);  CompletableFuture<String> future3 = future2.thenCompose(s ->   CompletableFuture.supplyAsync(() -> s + " composed")  );  // 等待 future3 完成并打印结果  // 注意:这里我们使用了 future3.get() 来阻塞主线程,以便看到结果  System.out.println(future3.get()); // "Initial value transformed composed"  }  
}

thenCompose()和thenApplyAsync区别:

处理异步操作链的方式:
thenCompose():该方法用于链接两个异步操作,当第一个操作完成时,将其结果作为参数传递给一个返回CompletableFuture的函数。这使得你可以创建一个平坦的结果链,即只有一个级别的CompletableFuture,而不是嵌套的CompletableFuture<CompletableFuture>。
thenApplyAsync():该方法也用于在异步计算完成后执行一个函数,但它返回的是一个新的CompletableFuture,并且这个函数接收的参数是前一个异步操作的结果。然而,与thenCompose()不同,thenApplyAsync()只是简单地将结果应用于一个函数,并返回一个新的CompletableFuture,而不是再次链接一个异步操作。
返回结果的方式:
thenCompose():返回的是一个新的CompletableFuture,这个新的CompletableFuture是由提供的函数返回的CompletableFuture。这意味着你可以在这个新的CompletableFuture上继续链式调用其他方法。
thenApplyAsync():同样返回一个新的CompletableFuture,但这个新的CompletableFuture是由提供的函数直接返回的结果(经过转换后)所创建的。它没有再次链接一个异步操作,只是简单地对结果进行转换。
用途:
thenCompose():当你想要将多个异步操作链接在一起,并且每个操作都返回一个新的CompletableFuture时,应该使用thenCompose()。这样可以避免嵌套的CompletableFuture结构,使代码更加清晰和易于理解。
thenApplyAsync():当你只需要对前一个异步操作的结果进行简单的转换,并返回一个新的结果时,应该使用thenApplyAsync()。它不会再次链接一个异步操作,只是简单地对结果进行转换。
执行方式:
thenCompose():执行方式取决于你提供的函数内部的操作。如果函数内部的操作是异步的(例如,它返回一个新的CompletableFuture),那么thenCompose()会等待这个异步操作完成后再继续执行后续的操作。
thenApplyAsync():默认情况下,thenApplyAsync()是异步执行的,它会将提供的函数提交到某个执行器(Executor)进行异步处理。但是,你也可以通过提供一个自定义的执行器来控制它的执行方式。
thenCompose()和thenApplyAsync()都是用于处理异步操作链的方法,但它们在处理方式和返回结果的方式上有所不同。thenCompose()更适合用于链接多个异步操作,而thenApplyAsync()则更适合用于对结果进行简单的转换。

CompletableFuture综合使用
需求:
订单上有商品ID,通过商品ID可以查询到商品详细信息,图片信息存储在商品详细信息中。
那就需要查询完订单再查询商品最后查询图片信息,这3个异步任务需要串行执行。

CompletableFuture<Order> orderFuture = fetchOrder(orderId); // 查询订单的异步方法  CompletableFuture<Product> productFuture = orderFuture.thenCompose(order -> {  // 使用订单中的商品ID来查询商品信息  List<Long> productIds = order.getProductIds(); // 订单有一个商品ID列表  // 简化只查询一个商品信息Long productId = productIds.get(0); return fetchProduct(productId); // 查询商品信息的异步方法  
});  CompletableFuture<Image> imageFuture = productFuture.thenCompose(product -> {  // 使用商品信息中的图片ID来查询图片信息  Long imageId = product.getImageId(); return fetchImage(imageId); //查询图片信息的异步方法  
});  // 等待图片信息查询完成并处理结果  
imageFuture.thenAccept(image -> {  System.out.println("Image URL: " + image.getUrl());  
}).exceptionally(throwable -> {  // 处理异常  throwable.printStackTrace();  return null;  
});

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

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

相关文章

Linux文件操作命令

马哥教育 Linux SRE 学习笔记 文件操作命令 显示当前工作目录 每个shell和系统进程都有一个当前的工作目录 CWD&#xff1a;current work directory 显示当前shell CWD的绝对路径 pwd命令: printing working directory -P 显示真实物理路径-L 显示链接路径&#xff08;默认&…

Light_Future的C++框架的套利代码读取arbitrage_strategy.cpp

1. 套利策略的初始化函数,订阅两个数据 void arbitrage_strategy::on_init(subscriber& suber) {// 向订阅者注册两个交易代码的tick接收器。suber.regist_tick_receiver(_code1, this);suber.regist_tick_receiver(_code2, this);// 获取当前交易日。uint32_t trading_d…

SpringCloud配置文件bootrap

解决方案&#xff1a; 情况一、SpringBoot 版本 小于 2.4.0 版本&#xff0c;添加以下依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-context</artifactId> </dependency> 情况二、SpringBoot…

[集群聊天服务器]----(八)群组类、群组操作接口以及业务模块之创建群组,加入群组以及群组聊天

接着上文关于[集群聊天服务器]----(七)业务模块之一对一聊天、添加好友函数、好友类以及离线消息类的剖析。本章将对创建群组&#xff0c;加入群组以及群组聊天业务进行剖析。 群类 类似于User类&#xff0c;构建了Group类 #ifndef GROUP_H #define GROUP_H#include "g…

在Windows中安装Redis

一、下载Redis github链接&#xff1a;https://github.com/redis-windows/redis-windows/releases 二、安装 解压后点击start.bat文件即可启动服务 新开一个cmd窗口进入安装了Redis的文件夹输入redis-cli.exe -h 127.0.0.1 -p 6379连接Redis&#xff0c;见如下结果便是成功&…

sql-labs靶场环境搭建(手把手保姆级教学)

文章目录 一、sql-labs靶场简介&#xff1a;二、搭建过程1、资源下载2、配置文件&#xff1b;3、访问网站4、创建数据库 三、使用PhpStudy2018原因 一、sql-labs靶场简介&#xff1a; SQL-Labs 是一个实践环境&#xff0c;旨在用于数据库和 SQL&#xff08;结构化查询语言&…

某大型制造集团企业信息化建设总体规划设计方案(67页PPT)

方案介绍&#xff1a; 随着信息技术的飞速发展&#xff0c;企业信息化建设已成为提高管理效率、增强企业竞争力的重要手段。某大型制造集团为应对市场变化、提升管理水平、优化资源配置&#xff0c;决定进行全面深入的信息化建设。本方案旨在构建一个集生产、管理、销售、物流…

Java中的IO和NIO(New IO)有什么区别?

在Java编程中&#xff0c;IO&#xff08;Input/Output&#xff09;和NIO&#xff08;New IO&#xff09;是两个重要的概念&#xff0c;它们分别代表了Java中的传统IO和新的IO库。为了全面解释这两者之间的区别&#xff0c;我们将从技术难点、面试官关注点、回答吸引力和代码举例…

【DevOps】Jenkins + Dockerfile自动部署Maven(SpringBoot)项目

环境 docker_host192.168.0.1jenkins_host192.168.0.2 jenkins_host构建完成后把jar发布到docker_host&#xff0c;再通过dockerfile自动构建镜像&#xff0c;运行镜像 1 Jenkins安装 AWS EC2安装Jenkins&#xff1a;AWS EC2 JDK11 Jenkins-CSDN博客 AWS EC2上Docker安装…

T113调试7寸RGB屏

文章目录 软硬件介绍软件板卡屏幕 调试修改内核设备树修改U-Boot设备树 测试添加启动logo其它问题总结 软硬件介绍 软件 基于Tina5.0 SDK。 板卡 韦东山的T113工业板&#xff1a; 屏幕 韦东山的7寸RGB电容触摸屏&#xff1a; 调试 修改内核设备树 打开内核设备树<…

代码随想录算法训练营第四天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点 、 面试题 02.07. 链表相交、142.环形链表II

24. 两两交换链表中的节点 题目链接&#xff1a; 24. 两两交换链表中的节点 文档讲解&#xff1a;代码随想录 状态&#xff1a;没做出来&#xff0c;没有正确更新头节点&#xff0c;因为head和cur共享引用&#xff0c;会随着cur的移动&#xff0c;丢失之前存放的节点 错误代码&…

efuse xinpian

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 xx项目需要进行efuse烧录&#xff0c;之前都是单板环境&#xff0c;现补充裸板烧录教…

Java计算时间相差多少秒/localdatetime计算时间差

1、常用的计算时间差方法&#xff1f; 【将字符串的时间转化成LocalDateTime】 String time1"2024-01-01 10:25:10";LocalDateTime starttime1 LocalDateTime.parse(time1, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));【LocalDateTime计算时…

51单片机-实机演示(按键)

书接上回。http://t.csdnimg.cn/4wSSW 目录 一.按下灭&#xff0c;松开亮 二.两个按键控制两个灯 三.点一下灯开&#xff0c;在按一下关 四。优化按键消抖 1.加入bit变量 一.按下灭&#xff0c;松开亮 代码 #include <reg52.h> //此文件中定义了单片机的一些特…

自定义函数python:深入解析与实操

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;函数的命名与规范 二、函数命名&#xff1a;遵循规范&#xff0c;易于…

利用阅读APP3.0目录展示要查看的内容01

喜欢读电子书的小伙伴往往会遇到一个问题&#xff0c;就是想要看书中某些内容&#xff0c;但是不知道具体章节&#xff0c;所以就用查找功能来查&#xff0c;但是呢查找功能查出来展示的结果并不直观。 比如想要阅读:青竹蜂云剑&#xff0c;大衍决&#xff0c;南宫婉&#xff0…

【wpf】关于焦点

背景 今天踩了一个坑。我用到handycontrol中的一个密码控件PasswordBox <hc:PasswordBox x:Name"pb" hc:TitleElement.Title"密码" hc:TitleElement.TitlePlacement"Left" Width"120"></hc:PasswordBox>然后想实现一个回…

基于springboot的在线宠物用品交易网站源码数据库

基于springboot的在线宠物用品交易网站源码数据库 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了在线宠物用品交易网站的开发全过程。通过分析在线宠物用品交易网站管理的不足&#xff0c;创建了一个计算机管理在…

265 基于matlab的粒子群优化分数阶灰色预测模型

基于matlab的粒子群优化分数阶灰色预测模型&#xff0c;以误差结果为目标进行预测&#xff0c;输出多个预测结果。并输出迭代曲线。程序已调通&#xff0c;可直接运行。 265 分数阶灰色预测 粒子群优化算法 - 小红书 (xiaohongshu.com)

二叉树——经典练习题

目录 前言&#xff1a; 一、单值二叉树 题目描述&#xff1a; 思路分析&#xff1a; 代码实现&#xff1a; 二、二叉树最大深度 题目描述&#xff1a; 思路分析&#xff1a; 代码实现&#xff1a; 三、检查两颗树是否相同 题目描述&#xff1a; 思路分析&#xff1a; 代…