Stream API

1.什么是 Stream API?

Stream API 是 Java 8 引入的一个用于操作集合数据的框架。它并不是数据的存储结构,而是一个高效的数据处理工具。Stream 提供了一种声明式、链式的方式,让我们能够对集合、数组等数据进行过滤、映射、排序、聚合等一系列复杂操作,并支持并行处理。

核心特点:

  • 声明式代码:通过链式操作替代繁琐的循环,使代码更加简洁。
  • 延迟执行:Stream 的中间操作不会立即执行,而是在需要结果时(终端操作)才会触发整个链式操作。
  • 并行处理:利用多核 CPU 的优势进行并行处理,大大提升数据处理的效率。
为什么是“流式”?

流(Stream)在这里的意义是“数据流”,即对数据进行流水线式的操作。它将数据按需逐步传递,从而避免了多次遍历和中间结果存储,具有延迟执行的特点。

Stream API 可以看作是一个流式数据处理的管道,这个管道包含多个阶段的操作,数据从一个阶段流向下一个阶段,直到达到终端操作时才会执行。这样减少了中间结果的生成,从而优化了处理效率。

延迟执行

Stream 的中间操作(如 filtermap)是惰性求值的,只有在终端操作(如 collectforEachreduce)执行时,所有操作才会一并执行。这样可以避免不必要的计算,提高性能。

例如:

List<String> words = Arrays.asList("stream", "java", "lambda", "code");
List<String> result = words.stream().filter(w -> w.length() > 4).map(String::toUpperCase).collect(Collectors.toList());

在上面代码中,filtermap 不会立即执行,只有在 collect 被调用时,整个流才会开始执行。这种延迟执行的特点确保了只有真正需要的数据才会被处理。

延迟执行可以避免不必要的计算,优化处理效率:

  • 减少数据的重复遍历:中间操作延迟进行了,然后等到最后要执行终端操作时,根据前面记录的中间操作,一次性就将所有中间操作完成,然后再执行终端操作,这样对所有元素只遍历了一次,减少遍历次数。
  • 按需处理数据:中间操作先不执行,系统自动根据最后的终端操作来判断前面是否有部分的中间操作没必要执行,比如如果最后的终端操作是返回第一个元素,那之前所有的中间操作都没必要执行了。
  • 动态优化操作链:可以在遍历完所有中间操作之后判断是否有些中间操作可以直接合并,这样就在一个管道流里直接完成执行,提高效率。
  • 提升并行化处理的效率:在并行流中,延迟执行让 Stream 可以更灵活地根据资源和数据量进行并行优化。

2.Stream API 的作用

1.简化代码逻辑

传统 Java 中,处理集合中的数据通常需要使用 for 循环或 Iterator 迭代,这种代码冗长且难以管理。Stream API 提供了一种更简洁的链式方法调用方式,让代码更具声明性(更像是在描述“要做什么”,而不是“怎么做”),从而提升代码的可读性。

例如,将列表中的偶数提取出来并求平方:

// 使用 Stream API
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers.stream().filter(n -> n % 2 == 0).map(n -> n * n).collect(Collectors.toList());

相比传统循环操作,Stream API 让代码更简洁清晰。

2.提供更灵活的操作

Stream API 提供了一系列丰富的操作,如 filter(筛选)、map(映射)、sorted(排序)、distinct(去重)等。这些操作可以组合起来完成复杂的数据处理任务,而无需大量循环和条件判断。

例如,你可以在一个流操作链中同时实现筛选、转换和聚合:

int sumOfEvenSquares = numbers.stream().filter(n -> n % 2 == 0).map(n -> n * n).reduce(0, Integer::sum);
3. 支持并行处理,提高效率

Stream API 支持并行流,即可以在多核 CPU 上并行地处理数据,充分利用硬件资源。对于大规模数据处理来说,并行流可以显著提高性能。

// 并行流操作
numbers.parallelStream().filter(n -> n % 2 == 0).forEach(System.out::println);

使用并行流时,流会自动拆分成多个子任务并行处理,利用多个线程分担计算负载,从而提升效率。


3.如何创建 Stream

Stream 可以从集合、数组、范围等多种数据源创建。以下是几种常见的创建方式:

1. 从集合创建

我们可以直接从 Java 的集合框架(如 ListSet)中获取流。

import java.util.*;
import java.util.stream.*;public class StreamCreationExample {public static void main(String[] args) {List<String> list = Arrays.asList("apple", "banana", "orange");Stream<String> stream = list.stream();  // 创建一个顺序流Stream<String> parallelStream = list.parallelStream();  // 创建一个并行流}
}
2. 从数组创建

可以通过 Arrays.stream() 方法从数组中创建流。

String[] array = {"a", "b", "c"};
Stream<String> streamFromArray = Arrays.stream(array);
3. 使用 Stream 静态方法创建

Stream API 提供了静态方法,如 Stream.of()Stream.iterate()Stream.generate() 等,用于直接创建流。

Stream<String> streamFromValues = Stream.of("apple", "banana", "orange");Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);  // 无限流,从 0 开始每次加 2
4. 创建数值范围流

IntStreamLongStream 等可以创建数值范围流,适用于需要生成连续数值的场景。

IntStream intStream = IntStream.range(1, 5); // 生成 1 到 4
IntStream intStreamInclusive = IntStream.rangeClosed(1, 5); // 生成 1 到 5

3.Stream API 的操作类型

Stream 的操作分为两类:中间操作终端操作。中间操作返回一个新的 Stream 对象(具有延迟计算特性),而终端操作触发流的执行,并返回最终结果或副作用。

延迟执行可以避免不必要的计算,优化处理效率:

  • 减少数据的重复遍历:中间操作延迟进行了,然后等到最后要执行终端操作时,根据前面记录的中间操作,一次性就将所有中间操作完成,然后再执行终端操作,这样对所有元素只遍历了一次,减少遍历次数。
  • 按需处理数据:中间操作先不执行,系统自动根据最后的终端操作来判断前面是否有部分的中间操作没必要执行,比如如果最后的终端操作是返回第一个元素,那之前所有的中间操作都没必要执行了。
  • 动态优化操作链:可以在遍历完所有中间操作之后判断是否有些中间操作可以直接合并,这样就在一个管道流里直接完成执行,提高效率。
  • 提升并行化处理的效率:在并行流中,延迟执行让 Stream 可以更灵活地根据资源和数据量进行并行优化。
1. 中间操作

中间操作是可以链式调用的操作,例如 filtermapsorteddistinct 等。中间操作是惰性求值的,即不会立即执行,而是等到执行终端操作时才会触发整个链的计算。

常见的中间操作包括:

  • filter:用于筛选符合条件的元素。
  • map:用于将每个元素转换为新的类型。
  • sorted:对流中的元素进行排序。
  • distinct:去除流中的重复元素。
  • limit:限制流中元素的数量。
  • skip:跳过流中的前 n 个元素。
2. 终端操作

终端操作会触发流的执行,并返回最终结果。常见的终端操作有:

  • collect:将流的结果收集到集合、列表等。
  • forEach:对流中的每个元素执行操作(例如打印)。
  • count:计算流中元素的数量。
  • reduce:将流中元素合并成一个值。
  • anyMatch / allMatch / noneMatch:用于检查流中元素是否符合某种条件。

4.常见方法详解

1. filter —— 过滤元素

filter 方法用于根据条件筛选出流中的元素,接受一个 Predicate 函数作为参数。

示例:筛选出长度大于 5 的水果名称

List<String> fruits = Arrays.asList("apple", "banana", "orange", "kiwi");List<String> filteredFruits = fruits.stream().filter(fruit -> fruit.length() > 5).collect(Collectors.toList());System.out.println(filteredFruits);  // 输出:[banana, orange]

解释:

  • filter(fruit -> fruit.length() > 5) 通过 Lambda 表达式定义筛选条件,将长度大于 5 的元素保留在流中。
  • collect(Collectors.toList()) 将结果收集为一个 List
2. map —— 转换元素

map 方法用于将流中的每个元素转换成另一种形式,接收一个 Function 函数作为参数。常用于将一种类型转换为另一种类型。

示例:将所有水果名称转换为大写

List<String> fruits = Arrays.asList("apple", "banana", "orange");List<String> upperCaseFruits = fruits.stream().map(String::toUpperCase).collect(Collectors.toList());System.out.println(upperCaseFruits);  // 输出:[APPLE, BANANA, ORANGE]

解释:

  • map(String::toUpperCase) 将每个元素转换为大写字母。
  • collect(Collectors.toList()) 将结果收集为一个 List
3. sorted —— 排序

sorted 方法用于对流中的元素进行排序,默认按自然顺序排序,也可以传入 Comparator 进行自定义排序。

示例:按字母顺序排序水果名称

List<String> fruits = Arrays.asList("banana", "apple", "orange");List<String> sortedFruits = fruits.stream().sorted().collect(Collectors.toList());System.out.println(sortedFruits);  // 输出:[apple, banana, orange]

解释:

  • sorted() 按自然顺序排序。
  • collect(Collectors.toList()) 将结果收集为 List
4. distinct —— 去重

distinct 方法用于去除流中的重复元素,特别适合去重需求。

示例:去除数字列表中的重复项

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);List<Integer> distinctNumbers = numbers.stream().distinct().collect(Collectors.toList());System.out.println(distinctNumbers);  // 输出:[1, 2, 3, 4, 5]

解释:

  • distinct() 去除了重复的数字。
  • collect(Collectors.toList()) 将结果收集为 List
5. limit —— 限制流的元素数量

limit 用于限制流中元素的数量。例如,如果你只想获取流中的前 n 个元素,可以使用 limit

示例:限制水果列表只保留前 2 个元素

List<String> fruits = Arrays.asList("apple", "banana", "orange", "kiwi");List<String> limitedFruits = fruits.stream().limit(2).collect(Collectors.toList());System.out.println(limitedFruits);  // 输出:[apple, banana]

解释:

  • limit(2) 将流中的元素限制为前 2 个。
  • collect(Collectors.toList()) 将结果收集为 List
6. skip —— 跳过流的元素

skip 用于跳过流中前 n 个元素,通常和 limit 搭配使用,适用于分页、截取数据等操作。

示例:跳过前 2 个水果,只保留之后的元素

List<String> fruits = Arrays.asList("apple", "banana", "orange", "kiwi");List<String> skippedFruits = fruits.stream().skip(2).collect(Collectors.toList());System.out.println(skippedFruits);  // 输出:[orange, kiwi]

解释:

  • skip(2) 将流中的前 2 个元素跳过,保留剩下的部分。
  • collect(Collectors.toList()) 将结果收集为 List

5.终端操作

终端操作触发流的执行,返回最终结果或产生副作用。常见的终端操作包括 collectforEachcountreduce 等。

1. collect —— 收集结果

collect 是最常用的终端操作之一,它用于将流中的元素收集到集合(如 ListSetMap)中,或者执行其他类型的收集操作(如统计等)。常与 Collectors 类的静态方法搭配使用。

示例:将流的结果收集为 List

List<String> fruits = Arrays.asList("apple", "banana", "orange");List<String> fruitList = fruits.stream().filter(fruit -> fruit.length() > 5).collect(Collectors.toList());System.out.println(fruitList);  // 输出:[banana, orange]

解释:

  • collect(Collectors.toList()) 将过滤后的流结果收集为一个 List

示例:将流的结果收集为 Set(自动去重)

List<String> fruits = Arrays.asList("apple", "banana", "orange", "apple");Set<String> fruitSet = fruits.stream().collect(Collectors.toSet());System.out.println(fruitSet);  // 输出:[banana, apple, orange]

解释:

  • collect(Collectors.toSet()) 将流结果收集为一个 Set,并自动去重。
2. forEach —— 遍历元素

forEach 用于遍历流中的每个元素,执行指定的操作。forEach 接收一个 Consumer,通常用来对流中的每个元素进行某种操作(如打印)。

示例:打印所有水果名称

List<String> fruits = Arrays.asList("apple", "banana", "orange");fruits.stream().forEach(System.out::println);

解释:

  • forEach(System.out::println) 将流中的每个元素逐一打印到控制台。

注意forEach 是终端操作,调用 forEach 后流就会关闭,不能再次操作流。

3. count —— 统计元素数量

count 用于计算流中的元素数量,返回一个 long 类型的值。

示例:统计水果列表中元素的数量

List<String> fruits = Arrays.asList("apple", "banana", "orange");long count = fruits.stream().count();System.out.println(count);  // 输出:3

解释:

  • count() 直接返回流中元素的数量,不做其他操作。
4. reduce —— 归约操作

reduce 用于将流中的元素合并成一个值,比如将数字加起来、计算乘积、拼接字符串等。reduce 接收一个 BinaryOperator 函数,适合用于聚合操作。

示例:计算数字列表中的总和

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);int sum = numbers.stream().reduce(0, Integer::sum);  // 初始值为 0,逐步累加System.out.println(sum);  // 输出:15

解释:

  • reduce(0, Integer::sum) 表示从初始值 0 开始,逐步将元素相加。
  • Integer::sumBinaryOperator 实现,表示加法操作。

示例:拼接所有水果名称

List<String> fruits = Arrays.asList("apple", "banana", "orange");String concatenatedFruits = fruits.stream().reduce("", (partialString, element) -> partialString + element);System.out.println(concatenatedFruits);  // 输出:applebananaorange

解释:

  • reduce("", (partialString, element) -> partialString + element) 将每个水果名称拼接在一起,得到完整的字符串。
5. anyMatchallMatchnoneMatch —— 条件匹配
  • anyMatch:判断流中是否有任意一个元素符合条件,返回 truefalse
  • allMatch:判断流中是否所有元素都符合条件,返回 truefalse
  • noneMatch:判断流中是否没有元素符合条件,返回 truefalse

示例:判断是否存在长度大于 5 的水果名称

List<String> fruits = Arrays.asList("apple", "banana", "orange");boolean hasLongFruit = fruits.stream().anyMatch(fruit -> fruit.length() > 5);System.out.println(hasLongFruit);  // 输出:true

解释:

  • anyMatch(fruit -> fruit.length() > 5) 表示检查流中是否有长度大于 5 的元素。

示例:判断所有水果名称是否都以小写字母开头

List<String> fruits = Arrays.asList("apple", "banana", "orange");boolean allLowerCase = fruits.stream().allMatch(fruit -> Character.isLowerCase(fruit.charAt(0)));System.out.println(allLowerCase);  // 输出:true

解释:

  • allMatch(fruit -> Character.isLowerCase(fruit.charAt(0))) 检查流中所有元素是否都符合小写开头的条件。

 6.并行流

在 Java Stream API 中,并行处理的功能可以通过 并行流 来实现。并行流会将数据自动分割成多个子流,并利用多线程进行并行计算,从而有效利用多核 CPU 的优势,提升数据处理的速度。

1.并行流的基本使用

在 Java 中,我们可以通过集合的 parallelStream() 方法或流对象的 parallel() 方法来创建并行流。并行流会将操作分发到多个线程,并在后台自动管理这些线程的任务。

通过 parallelStream() 方法创建并行流

集合类(如 ListSet 等)提供了 parallelStream() 方法,可以直接创建一个并行流。

List<String> fruits = Arrays.asList("apple", "banana", "orange", "kiwi");fruits.parallelStream().forEach(System.out::println);

在上面的代码中,parallelStream() 方法会将 fruits 列表分割成若干子任务,然后在多个线程中并行打印每个元素。这种方式可以有效地利用多核 CPU。

通过 parallel() 方法将顺序流转换为并行流

如果你已经有一个普通的顺序流,也可以通过 parallel() 方法将其转换为并行流。

Stream<String> fruitStream = Stream.of("apple", "banana", "orange", "kiwi");fruitStream.parallel().forEach(System.out::println);

在这里,parallel() 将顺序流 fruitStream 转换成了并行流,之后的操作将会在多个线程中并行执行。


2.并行流的底层原理

并行流的实现原理是基于 Java Fork/Join 框架,它会自动将流分成多个子任务,然后在多个 CPU 核心上同时处理每个子任务。当子任务完成后,再将各自的结果合并成最终的结果。

这种方式避免了手动创建和管理线程的复杂性,由 Java 虚拟机负责线程管理,程序员只需专注于编写流操作。


3.并行流的使用示例

下面是一些实际使用并行流的场景,展示如何利用并行流来提升性能。

示例 1:计算平方和

假设有一个大规模的整数列表,我们希望计算所有整数平方的总和。并行流可以在多个线程中计算每个整数的平方,并将结果合并,显著提升计算速度。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);int sumOfSquares = numbers.parallelStream().map(n -> n * n).reduce(0, Integer::sum);System.out.println("Sum of squares: " + sumOfSquares);

在这个例子中:

  • parallelStream() 创建了一个并行流,将列表分为多个子任务。
  • map(n -> n * n) 计算每个数字的平方。
  • reduce(0, Integer::sum) 将所有平方的结果汇总成一个总和。

示例 2:过滤和统计

假设有一个包含大量数据的字符串列表,我们希望统计其中包含指定字符的字符串数量。并行流可以让每个线程处理部分数据,然后汇总结果。

List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape");long count = words.parallelStream().filter(word -> word.contains("e")).count();System.out.println("Number of words containing 'e': " + count);

在这个例子中:

  • parallelStream() 会将列表中的数据分割,让多个线程并行执行 filter(word -> word.contains("e"))
  • 最终结果是统计包含字符 e 的字符串总数。

示例 3:字符串拼接

使用并行流也可以完成字符串拼接,适用于大数据量的字符串集合处理。

List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape");String result = words.parallelStream().reduce("", (partialString, element) -> partialString + element);System.out.println("Concatenated string: " + result);

在这个例子中:

  • parallelStream() 创建一个并行流。
  • reduce 操作将每个字符串拼接成一个完整的字符串,多个线程并行执行字符串拼接操作。
4.并行流的注意事项

尽管并行流可以提高性能,但在某些场景下并不适用,需要注意以下几点:

  1. 数据量较小时不建议使用:对于小规模数据,并行流带来的线程管理开销可能会超过其带来的性能收益。

  2. 顺序依赖的操作不适用:某些顺序依赖操作(如列表的排序、索引访问)不适合并行流,可能会导致结果不准确。

  3. 非线程安全操作要谨慎:如果流操作中包含修改共享状态的操作,并行流可能导致数据冲突或不一致,应避免非线程安全的操作。

  4. 避免使用 forEachOrderedforEachOrdered 强制按顺序执行操作,可能会降低并行流的性能。

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

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

相关文章

【Linux学习】【Ubuntu入门】1-3 ubuntu连接USB设备

1.打开VMware&#xff0c;打开新建的虚拟机&#xff0c;插入U盘&#xff0c;可在弹出对话框进行选择USB连接到主机或连接到虚拟机。&#xff08;长时间未操作默认连接主机&#xff09; 2.若USB在连接主机的情况下&#xff0c;可通过右键点击右下角进行连接到虚拟机。 3.若已连接…

炼码LintCode--数据库--基础语法--刷题笔记_01

目录 炼码LintCode数据库入门级别的笔记未完待续~~~ 炼码LintCode 数据库 入门级别的笔记 笔记如下&#xff0c;把所有涉及到的入门级别的知识点简单总结了一下。 以及一点点举一反三的写法。 增 INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);批量增 INSERT INT…

docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled

无数次的拉镜像让人崩溃&#xff1a; rootnode11:~/ragflow/docker# more rag.sh #export HTTP_PROXYhttp://192.168.207.127:7890 #export HTTPS_PROXYhttp://192.168.207.127:7890 #export NO_PROXYlocalhost,127.0.0.1,.aliyun.com docker compose -f docker-compose-gpu-C…

Flutter:使用Future发送网络请求

pubspec.yaml配置http的SDK cupertino_icons: ^1.0.8 http: ^1.2.2请求数据的格式转换 // Map 转 json final chat {name: 张三,message: 吃饭了吗, }; final chatJson json.encode(chat); print(chatJson);// json转Map final newChat json.decode(chatJson); print(newCha…

llama-cpp模型轻量化部署与量化

一、定义 定义配置环境遇到的问题&#xff0c;交互模式下模型一直输出&#xff0c;不会停止模型量化Qwen1.5-7B 案例demo 二、实现 定义 主要应用与cpu 上的部署框架。由c完成。配置环境 https://github.com/ggerganov/llama.cpp https://github.com/echonoshy/cgft-llm/blo…

阅读《当代反无人机系统技术综述》笔记

目录 文献基本信息 序言 一、关键技术 1.1射频(RF)分析仪 1.2雷达 1.3视觉传感器和图像处理 1.4声学传感器 二、发展趋势 文献基本信息 题名&#xff1a;当代反无人机系统技术综述 作者&#xff1a;蒋罗婷 来源&#xff1a;电子质量 发表时间&#xff1a;2023-02-2…

【Lucene】倒排表和词典:提升搜索效率的关键数据结构

倒排表和词典&#xff1a;提升搜索效率的关键数据结构 倒排表&#xff08;Inverted Index&#xff09;和词典&#xff08;Term Dictionary&#xff09;是 Lucene 中用于加速搜索的关键数据结构&#xff0c;它们帮助系统在庞大的文档集合中快速定位包含特定关键词的文档。以下是…

RN开发遇到的坑

1 、 RN 启动崩溃 https://blog.csdn.net/qq_31915745/article/details/108125671 2、修改报红⻚ https://blog.csdn.net/weixin_43969056/article/details/104757926 3 、编译不过去提示 glog-0.3.5 有问题&#xff0c; 找到 / 项⽬ /node_modules/react-native/scripts/ io…

基于Multisim信号波形发生器电路正弦波方波三角波锯齿波(含仿真和报告)

【全套资料.zip】正弦方波三角波锯齿波方波占空比可调频率可调电路Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.设计一个能够产生多个信号输出的信号发生器&#xff0c; 要求输出波形…

GC9A01驱动移植(HALL库)

最近在做LVGL的移植&#xff0c;用到的屏幕驱动时GC9A01的&#xff0c;记录一下学习历程&#xff0c;防止日后遗忘。 这款屏幕我使用的是SPI协议&#xff0c;参考了部分稚辉菌大佬这个项目的程序&#xff1a; 【自制】我做了个能动的迷你电脑配件&#xff01;【软核】_哔哩哔…

PCIe板卡标准尺寸

一、板卡尺寸说明 两种PCIe外接卡的高度&#xff0c;即全高&#xff08;Standard height&#xff09;111.15 mm (4.376 inches)和半高&#xff08;half height&#xff09;68.90 mm (2.731 inches)&#xff1b; 两种PCIe外接卡的长度&#xff1a;全长&#xff08;full length&a…

docker 阿里云镜像加速

在阿里云首页点击产品-容器-容器镜像服务ACR 无需购买&#xff0c;直接进去控制台创建个人版 完成后点击镜像加速器 选择对应的系统&#xff0c;按照操作文档完成

KPaaS洞察|异构系统中用户角色与权限分类及管理解决方案

多个异构系统的使用已经成为企业常态。每个系统通常有自己独立的用户角色和权限设置&#xff0c;导致权限管理复杂且容易出现冲突。如何在多个异构系统中统一、有效地进行用户角色和权限管理&#xff0c;已成为企业保障数据安全和提升管理效率的关键挑战。通过集中式权限管理平…

食品配送管理系统(源码+文档+部署+讲解)

食品配送管理系统是成品商业化项目&#xff0c;系统可基于源码二开。 系统概述 餐饮食品配送&#xff0c;包含配送人APP、下单APP、管理端等&#xff0c;实现订餐、配餐&#xff0c;用于食品店、中央厨房等订餐、团餐业务 本项目名称为食品配送系统&#xff0c;是针对食品配…

领夹麦克风哪个品牌音质最好?无线领夹麦克风可以唱歌吗?

随着短视频和直播行业的蓬勃发展&#xff0c;无线领夹麦克风已成为内容创作者提升音质体验的关键一环。但遗憾的是&#xff0c;市场上充斥着太多夸大其词、华而不实的宣传&#xff0c;诸如“一键降噪”、“无损传输”等概念满天飞&#xff0c;让消费者难以分辨真伪。许多朋友在…

大模型学习笔记------BLIP模型的再思考

大模型学习笔记------BLIP模型的再思考 1、BLIP推理---如何进行“图生文”2、BLIP推理---如何进行视觉问答&#xff08;Visual Question Answering, VQA&#xff09;3、BLIP推理---如何进行图文检索&#xff08;Image-text retrieval&#xff09;任务4、总结 上一篇文章上文中讲…

c# 调用c++ 的dll 出现找不到函数入口点

今天在调用一个设备的dll文件时遇到了一点波折&#xff0c;因为多c 不熟悉&#xff0c;调用过程张出现了找不到函数入口点&#xff0c;一般我们使用c# 调用c 文件&#xff0c;还是比较简单。 [DllImport("AtnDll2.dll",CharSet CharSet.Ansi)]public static extern …

H5BuildX发行uniapp h5版本的正确姿势

在manifest.json中配置基础路径 在上传到服务器后&#xff0c;需要将打包后的文件夹修改为基础路径中相同的文件名 否则网页的css、js等资源文件会因为路径问题始终访问不了

C++(Qt)软件调试---符号转换工具cv2pdb (24)

C(Qt)软件调试—符号转换工具cv2pdb &#xff08;24&#xff09; 文章目录 C(Qt)软件调试---符号转换工具cv2pdb &#xff08;24&#xff09;[toc]1、概述&#x1f41c;2、下载cv2pdb&#x1fab2;3、qt程序使用cv2pdb&#x1f9a7;1.1 方法1&#xff1a;命令行1.2 方法2&#…

MySQL技巧之跨服务器数据查询:高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;高级篇-先调用A数据库的MySql存储过程再复制到B数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的…