【昕宝爸爸小模块】深入浅出之Java 8中的 Stream

在这里插入图片描述

深入浅出之Java 8中的 Stream

  • 一、🟢典型解析
    • 1.1 🟠Java 8中的Stream 都能做什么
    • 1.2 🟠Stream的创建
  • 二、✅ Stream中间操作
    • 2.1 🟠Filter
    • 2.2 🟠Map
    • 2.3 🟠limit / skip
    • 2.4 🟠sorted
    • 2.5 🟠distinct
  • 三、 ✅Stream最终操作
    • 3.1 🟠forEach
    • 3.2 🟠count
    • 3.3 🟠collect
  • 四、✅ 扩展知识仓
    • 4.1 🟠Stream有哪些优点和缺点
    • 4.2 🟠Stream中间操作的作用是什么
    • 4.3 🟠Stream终极操作的作用是什么


一、🟢典型解析


1.1 🟠Java 8中的Stream 都能做什么


Stream 使用一种类似用 SOL 语句从数据库查询数据的直观方式来提供一种对Java 集合运算和表达的高阶抽象。


Stream API 可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。


这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。


Stream有以下特性及优点


  • 无存储。Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器I/O channel 等。
  • 为函数式编程而生。对 Stream 的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
  • 惰式执行。Stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • 可消费性。Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要重次遍历必须重新生成。

我们举一个例子,来看一下到底Stream可以做什么事情:


在这里插入图片描述


上面的例子中,获取一些带颜色塑料球作为数据源,首先过滤掉红色的、把它们融化成随机的三角形。再过滤器并删除小的三角形。最后计算出剩余图形的周长。


如上图,对于流的处理,主要有三种关键性操作: 分别是流的创建中间操作 (intermediateoperation) 以及最终操作(terminal operation)


1.2 🟠Stream的创建


在Java 8中,可以有多种方法来创建流。


1、通过已有的集合来创建流


在Java 8中,除了增加了很多Stream相关的类以外,还对集合类自身做了增强,在其中增加了stream方法,可以将一个集合类转换成流。


List<String> strings = Arrays.asList("Java", "JavaAndJ", "Java", "Hello", "HelloWorld","Java");

 Stream<String> stream = strings.stream();

以上,通过一个已有的List创建一个流。除此以外,还有一个parallelStream 方法,可以为集合创建个并行流。


这种通过集合创建出一个Stream的方式也是比较常用的一种方式。


2、通过Stream创建流


可以使用Stream类提供的方法,直接返回一个由指定元素组成的流。


Stream<String> stream = Stream.of("Java", "JavaAndJ", "Java", "Hello", "HelloWorld","Java");

如以上代码,直接通过 of 方法,创建并返回一个Stream。


二、✅ Stream中间操作


Stream有很多中间操作,多个中间操作可以连接起来形成一个流水线,每一个中间操作就像流水线上的一个工人,每人工人都可以对流进行加工,加工后得到的结果还是一个流。


在这里插入图片描述

以下是常用的中间操作列表:


在这里插入图片描述

2.1 🟠Filter


Stream的中间操作Filter的作用是过滤流中的元素,它接受一个Predicate接口作为参数,该接口中的test方法可以对给定的参数进行判断,返回一个布尔值。通过调用filter方法,我们可以从流中过滤出满足条件的元素。

在Java中,我们可以通过以下步骤使用Filter中间操作:

1. 创建Stream对象,对需要过滤的集合对象使用stream().filter(Predicate)方法。

2. 传入一个Lambda表达式作为参数,该Lambda表达式定义了过滤条件。

3. 执行终止操作,如collect、forEach等,将过滤后的结果输出或处理。


看一个Demo:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
*   演示了如何使用Java Stream API进行一系列的中间操作和终止操作
*   来处理一个学生列表,包括筛选特定条件的学生、统计满足条件的
*  学生数量、计算平均分
*/
public class StreamComplexExample {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", 85),
            new Student("Bob", 90),
            new Student("Charlie", 78),
            new Student("David", 88),
            new Student("Eve", 92)
        );
        
        // 筛选出平均分大于等于85的学生,并计算满足条件的学生数量
        int count = students.stream()
                             .filter(student -> student.getScore() >= 85)
                             .collect(Collectors.toList())
                             .size();
        System.out.println("满足条件的学生数量: " + count); // 输出: 3
        
        // 计算平均分大于等于85的学生的平均分
        double averageScore = students.stream()
                                       .filter(student -> student.getScore() >= 85)
                                       .mapToDouble(Student::getScore) // 将学生对象转换为分数
                                       .average()                      // 计算平均分
                                       .orElse(0);                     // 如果流为空,返回0
        System.out.println("平均分: " + averageScore); // 输出: 88.33333333333334
        
        // 根据性别分组,并计算每个性别的平均分
        Map<String, Double> averageScores = students.stream()
                                                     .collect(Collectors.groupingBy(Student::getGender,
                                                         Collectors.averagingDouble(Student::getScore)));
        System.out.println("平均分(按性别): " + averageScores); 


		// 输出: {男=87.5, 女=86}
    }
}

2.2 🟠Map


Stream的中间操作Map的作用是将流中的每个元素转换成另一个对象,并返回一个新的流。Map操作接受一个Function接口作为参数,该接口中的apply方法可以对给定的参数进行转换,并返回转换后的结果。

在Java中,我们可以通过以下步骤使用Map中间操作:

  1. 创建Stream对象,对需要映射的集合对象使用stream().map(Function)方法。
  2. 传入一个Lambda表达式或方法引用作为参数,该Lambda表达式或方法引用定义了映射规则。
  3. 执行终止操作,如collect、forEach等,将映射后的结果输出或处理。

Demo:


import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* 使用Java Stream API进行一系列的中间操作和终止操作来处理一个
* 整数列表,包括过滤出偶数、计算每个偶数的平方、以及找出平方值
* 最大的那个偶数
*/
public class StreamComplexExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 过滤出偶数,并计算每个偶数的平方
        List<Integer> squaredEvens = numbers.stream()
                                              .filter(number -> number % 2 == 0)
                                              .map(number -> number * number)
                                              .collect(Collectors.toList());
        System.out.println("平方的偶数: " + squaredEvens); // 输出: [4, 16, 36, 64]
        
        // 找出平方值最大的那个偶数
        Optional<Integer> maxSquare = squaredEvens.stream()
                                                   .max(Integer::compare);
        if (maxSquare.isPresent()) {
            System.out.println("平方值最大的偶数: " + maxSquare.get()); // 输出: 64
        } else {
            System.out.println("没有找到平方值最大的偶数");
        }
    }
}

//    输出结果:
//    平方的偶数: [4, 16, 36, 64]  
//    平方值最大的偶数: 64

2.3 🟠limit / skip


在Java的Stream API中,limit是一个中间操作,用于限制流中的元素数量。这个操作返回一个新的流,其中只包含原始流中的前N个元素。


Demo:


import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* 如何使用Java Stream API进行一系列的中间操作和终止操作来处理
* 一个字符串列表,包括将字符串转换为小写、过滤出长度大于等于5的
* 字符串、计算每个字符串的长度、找出长度最大的字符串
*/
public class StreamComplexExample {
    public static void main(String[] args) {
        List<String> strings = Arrays.asList("Hello", "World", "Java", "Stream", "Example");
        
        // 将字符串转换为小写,并过滤出长度大于等于5的字符串
        List<String> lowercaseStrings = strings.stream()
                                                .map(String::toLowerCase) // 将字符串转换为小写
                                                .filter(string -> string.length() >= 5) // 过滤出长度大于等于5的字符串
                                                .collect(Collectors.toList()); // 收集结果到列表
        System.out.println("Lowercase strings: " + lowercaseStrings); // 输出: [hello, world, example]
        
        // 计算每个字符串的长度,并找出长度最大的字符串
        Optional<String> longestString = lowercaseStrings.stream()
                                                           .max(String::compareTo); // 比较字符串长度
        if (longestString.isPresent()) {
            System.out.println("Longest string: " + longestString.get()); // 输出: Example
        } else {
            System.out.println("No longest string found");
        }
    }
}

//输出结果:
//Lowercase strings: [hello, world, example]  
//Longest string: Example

2.4 🟠sorted


在Java的Stream API中,sorted是一个中间操作,用于对流中的元素进行排序。它可以按照自然顺序进行排序,也可以根据指定的比较器进行排序。


Demo:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 使用Java Stream API的`sorted`中间操作对整数列表进行排序
*/
public class StreamSortedIntegerExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 7);

        // 使用sorted中间操作对整数进行排序
        List<Integer> sortedNumbers = numbers.stream()
                                              .sorted()
                                              .collect(Collectors.toList());

        System.out.println(sortedNumbers); // 输出: [1, 2, 5, 7, 9]

        // 如果你想降序排序,可以使用Comparator.reverseOrder()
        List<Integer> sortedDescending = numbers.stream()
                                                .sorted(Comparator.reverseOrder())
                                                .collect(Collectors.toList());

        System.out.println(sortedDescending); // 输出: [9, 7, 5, 2, 1]
    }
}

注意:sorted方法不会修改原始流中的元素,而是返回一个新的流,其中包含排序后的元素。要查看排序结果,需要使用一个终止操作(在这个例子中是collect)来收集流中的元素。


2.5 🟠distinct


在Java的Stream API中,distinct是一个中间操作,用于去除流中的重复元素。


Demo:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 使用`distinct`中间操作
*/
public class StreamDistinctExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
        
        // 使用distinct中间操作去除重复元素
        List<Integer> distinctNumbers = numbers.stream()
                                                 .distinct()
                                                 .collect(Collectors.toList());
        
        System.out.println(distinctNumbers); // 输出: [1, 2, 3, 4, 5]
    }
}

例子中,创建了一个包含七个整数的列表numbers,其中有两个2和两个4。然后,我们使用stream().distinct()将流中的重复元素去除,并将结果收集到一个新的列表distinctNumbers中。最后,我们打印出这个去重后的列表。


接下来我们通过一个例子和一张图,来演示下,当一个Stream先后通过filter、map、sort、limit以及distinct处理后会发生什么。


代码如下:


List<String> strings = Arrays.aslist("Java", "JavaAndJ", "Java", "Hello", "HelloWorld","Java");

Stream s = strings.stream(),filter(string -> string.length( )<= 6)
	.map(string::length).sorted()
	.limit(3)
	.distinct();

过程及每一步得到的结果我已经给大家画出来了,帮助大家快速掌握:


在这里插入图片描述

三、 ✅Stream最终操作


Stream的中间操作得到的结果还是一个Stream,那么如何把一个Stream转换成我们需要的类型呢? 比如计算出流中元素的个数、将流装换成集合等。这就需要最终操作 (terminal operation)


最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能在使用任何中间操作,否则将抛出异常:


java.lang.IllegalStateException: stream has already been operated upon or closed


俗话说,“你永远不会两次踏入同一条河” 也正是这个意思。


常用的最终操作如下图:


在这里插入图片描述

3.1 🟠forEach


Stream 提供了方法 "forEach’来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:


Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

3.2 🟠count


count用来统计流中的元素个数。


List<String> strings = Arrays.aslist("Java", "JavaAndJ","Java666", "Java", "Hello", "HelloWorld","Java");

System.out.printIn(strings.stream().count()); //7

3.3 🟠collect

List<String> strings = Arrays.asList ("Java", "JavaAndJ","Java666", "Java", "Hello", "HelloWorld","Java");

strings = strings.stream().filter(string -> string.startsWith("Hollis")).collect(Collectors.tolist());
System.out.println(strings);

接下来,我们还是使用一张图,来演示下,前文的例子中,当一个Stream先后通过filter、map.sort、limit以及distinct处理后会,在分别使用不同的最终操作可以得到怎样的结果。


下图,展示了文中介绍的所有操作的位置、输入、输出以及使用一个案例展示了其结果:


在这里插入图片描述

四、✅ 扩展知识仓


4.1 🟠Stream有哪些优点和缺点


Stream的优点

  1. 声明性:Stream允许我们以声明性的方式处理数据,这意味着我们只需关注需要做什么,而不是如何做。这使得代码更加简洁和易读。
  2. 可复合性:Stream的操作是可复合的,这意味着多个操作可以链式调用,从而简化了代码。
  3. 可并行处理:Stream操作可以并行执行,这使得处理大量数据更加高效。
  4. 延迟计算:Stream操作在需要结果时才执行,这使得计算更加高效。
  5. 操作转换:Stream提供了一种简单而强大的方式来对数据进行过滤、转换、排序和聚合等操作。

Stream缺点

  1. 数据不可变性:Stream操作返回的是新的Stream对象,而不是修改原始数据集。这意味着每次操作都会创建一个新的数据集,这可能会导致内存和性能问题,特别是对于大规模数据集。
  2. 错误处理:当Stream操作发生错误时,需要特别小心处理。由于Stream操作是惰性计算的,一旦发生错误,可能需要重新计算整个数据集才能找到问题所在。
  3. 类型安全:在使用Stream时,需要特别注意类型安全。由于Stream操作是惰性计算的,类型信息在计算过程中可能会丢失,从而导致类型错误。
  4. 函数式编程的限制:Stream是基于函数式编程的,这使得代码更加简洁和易读。但是,这也意味着某些传统的编程模式可能不适用。例如,循环和条件语句在某些情况下可能比使用Stream更简单和直观。

4.2 🟠Stream中间操作的作用是什么


Stream中间操作在Java中扮演着处理数据流的重要角色。它们的主要作用是在数据流上进行一系列转换和操作,以产生一个新的数据流供后续操作使用。中间操作不会立即执行实际的数据处理,而是创建一个新的流,并在其中定义将要执行的操作。这种特性被称为“惰性求值”或“延迟执行”,意味着只有在终端操作被触发时,中间操作才会真正开始执行。


中间操作可以包括过滤、映射、排序、去重等各种转换操作。例如,使用filter方法可以根据给定的条件过滤流中的元素,只保留满足条件的元素。map方法则用于对流中的每个元素应用一个函数,将其转换成另一种形式。这些中间操作可以链式调用,形成一个处理管道,对流进行多次转换。


通过中间操作,可以逐步构建和调整数据流,以满足特定的处理需求。最终,通过终端操作(如collectforEach等)触发实际的数据处理,并产生最终结果或副作用。这种声明性的处理方式使得代码更加简洁、易读,并有助于提高代码的可维护性和可重用性。


4.3 🟠Stream终极操作的作用是什么


Stream的终极操作会消耗流并产生一个最终结果。这意味着一旦进行终极操作,就不能再次使用流,也不再使用任何中间操作,否则会抛出异常。常见的终极操作包括计算出流中元素的个数、将流转换成集合、以及遍历流中的元素等。


终极操作的主要作用是完成数据流的处理,并产生所需的结果。例如,通过collect操作将流转换为集合,或使用forEach操作对每个元素执行某些操作。一旦执行了终极操作,流中的数据就被处理完毕,并且流对象本身通常会被丢弃或不再使用。


注意:在Java中,如果流支持迭代器(iterator),那么在调用iterator()方法后,流就不能再被使用,否则会抛出IllegalStateException异常。这是因为迭代器会消耗流中的元素,并且不支持逆向操作。因此,在使用迭代器时,必须小心处理流的剩余部分,以避免潜在的错误和异常。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FFmpeg编程录制音频(Mac OS)

之前我们使用FFmpeg命令行工具进行了简单的音视频操作&#xff0c;这次在Mac OS环境下编写代码实现简单的音频录制功能。 FFmpeg命令行音频录制 首先回顾一下Mac OS环境下简单的音频录制命令行实现&#xff1a; ffmpeg -f avfoundation -i ":0" -t 20 -acodec pcm…

电商平台如何引爆用户自主裂变:从策略到实践的全面解析

在当今竞争激烈的电商市场中&#xff0c;用户裂变成为企业持续增长的关键。如何引导用户自发传播&#xff0c;实现口碑与销量的双赢&#xff0c;是电商平台必须面对的挑战。本文将深入探讨电商平台如何通过精心策划和实施策略&#xff0c;激发用户自主裂变&#xff0c;助力企业…

蓝屏代码0x000007E解决办法

概述 出现该问题&#xff1a; 1、硬件冲突造成的蓝屏 驱动冲突&#xff1a;与其他设备或应用程序的驱动冲突可能会引起系统崩溃。 2、内存虚拟不足造成的蓝屏 错误配置&#xff1a;不正确的配置或设置可能会导致蓝屏错误。 3、超频后也可能出现蓝屏 CUP超频或者显卡超频后出现蓝…

水汽稳定度修正函数\Psi_q对潜热通量影响--模式验证工作

我之前提出了一个水汽通量廓线关系&#xff0c;这项工作偏理论&#xff0c;如果对下面说的背景不了解的话可以看下 https://agupubs.onlinelibrary.wiley.com/share/YNSG74MV8B8BAAUMCHN3?target10.1029/2022JD036708 那会没把提出的水汽稳定度修正函数加到CAS-ESM,当时对CAS-…

Ubuntu20.04-剪贴板

针对图形界面用户 1.两种方式 1.1 安装Parcellite 简单轻量级剪贴板管理器 sudo apt install parcellite 1.2 安装Gpaste 更强大的剪贴板管理器&#xff0c;包含历史记录和同步功能 sudo apt install gpaste

仿真验证方法(1)——动态验证

一、概述 1.1 验证的目的和方法 在现代集成电路设计中&#xff0c;验证所占工作量超过70%。验证要求真实而完备&#xff0c;它决定了设计的成败与成本。 验证的目的 原始描述是否正确&#xff1f;&#xff08;代码&#xff09; 逻辑功能是否正确&#xff1f;&#xff08;功能…

MATLAB--pie函数绘制分类饼图(1)--附案例代码

MATLAB–pie函数绘制分类饼图&#xff08;1&#xff09; 目录 MATLAB--pie函数绘制分类饼图&#xff08;1&#xff09;摘要1. pie函数概述2. 使用pie函数绘制分类图的步骤步骤1&#xff1a;准备数据步骤2&#xff1a;调用pie函数步骤3&#xff1a;定制图形&#xff08;可选&…

从事铁路工作保护足部,穿什么劳保鞋更安全

铁路运输在我国交通运输业中起着骨干作用&#xff0c;为国民经济的可持续发展和人口流动做出了巨大贡献。安全是铁路运输不可忽视的问题&#xff0c;在作业场地随处能见到“安全就是生命&#xff0c;责任重于泰山”的安全标语&#xff0c;由此可见安全问题是放在首位的。 铁路施…

2023年全球软件质量效能大会(QECon上海站):核心内容与学习收获(附大会核心PPT下载)

会议聚焦于软件质量和效能的提升。在智能时代&#xff0c;随着数字化的深入人心&#xff0c;软件正在随着云计算、移动互联网、物联网等的发展而不断进化&#xff0c;软件对企业的发展愈加重要&#xff0c;大家对软件的质量要求也在从传统功能、性能、安全这些基础层面向着用户…

GVM垃圾收集器

Serial收集器&#xff08;新生代&#xff09; Serial&#xff08;串行&#xff09;收集器是最基本、历史最悠久的垃圾收集器&#xff0c;采用“标记-复制”算法负责新生代的垃圾收集。它是Hotspot虚拟机运行在客户端模式下的默认新生代收集器。 它是一个单线程收集器。它会使用…

C桑(Cython)从入门到入土(2): np数组操作对比

C桑(Cython)从入门到入土 2. np数组操作对比 Node sc518 Copy has image data of type unsigned long long实际上, &#x1f446;&#x1f3fb;这个错误是我探索Cython的缘起… code1: Form: 更现代的方法是使用内存视图而不是指针&#xff1a; cdef np.uint32_t[:,:,::1] …

大模型学习产品,一个月顶一年 | 对话网易有道周枫

OpenAI CEO奥特曼曾表示&#xff1a;“AI女友只不过是一个美丽的陷阱&#xff0c;AI教育才是最应该去发力的一个领域。” 场景的确定性&#xff0c;是OpenAI等一众公司尤为重视教育领域的原因所在。教与学是教育场景中的核心&#xff0c;但再将两个字进行拆解&#xff0c;教学…

展望2024:9大要点把握PLM软件趋势,云PLM领导者Arena

2023年《质量强国建设纲要》&#xff08;以下简称《纲要》&#xff09;的推出&#xff0c;再次确定了中国要走上制造业高质量发展之路的决心。《纲要》指出要深入实施质量强国战略&#xff0c;加快传统制造业技术迭代和质量升级&#xff0c;推动工业品质量迈向中高端。当前&…

蓝桥杯基础知识3 memset()

蓝桥杯基础知识3 memset() #include <bits/stdc.h> using namespace std;int main(){int a[5]; //随机数for(int i 0;i < 5; i)cout << a[i] << \n;cout << \n;memset(a, 0, sizeof a); //0for(int i 0;i < 5; i)cout << a[i] << …

高德打车引入“红绿灯倒计时”能力,算力技术升级打车体验

打到的车还有多久能到&#xff1f;接驾车辆原地不动是什么原因&#xff1f;乘客在打车时&#xff0c;常常因为无法了解实时接驾路况&#xff0c;容易出现“等车焦虑”。 如今&#xff0c;高德打车已全面应用“红绿灯倒计时”能力&#xff0c;让乘客在等车时就能掌握接驾路况&am…

Qt QTableWidget表格控件

文章目录 1 属性和方法1.1 行列数目和行表头和列表头1.2 单元格1.3 隔行交替背景色1.4 选择模式和选择行为1.5 设置样式表 2 实例2.1 布局2.2 代码实现 QTableWidget是Qt中的表格控件。 1 属性和方法 QTableWidget有很多属性和方法&#xff0c;完整的可查看帮助文档。 在窗口…

时间序列预测 — VMD-LSTM实现单变量多步光伏预测(Tensorflow):单变量转为多变量预测多变量

专栏链接&#xff1a;https://blog.csdn.net/qq_41921826/category_12495091.html 专栏内容 所有文章提供源代码、数据集、效果可视化 文章多次上领域内容榜、每日必看榜单、全站综合热榜 时间序列预测存在的问题 现有的大量方法没有真正的预测未来值&#xff0c;只是用历史数据…

STM32L051使用HAL库操作实例(14)- ADC采集电压

目录 一、前言 二、ADC外设简要说明 三、STM32CubeMX配置&#xff08;本文使用的STM32CubeMX版本为6.1.2&#xff09; 1.MCU选型 2.时钟使能 3.外部时钟配置 4.串口配置 5.ADC引脚配置 6.配置STM32CubeMX生成工程文件 7.点击GENERATE CODE生成工程文件 四、工程源码 …

20240112让移远mini-PCIE接口的4G模块EC20在Firefly的AIO-3399J开发板的Android11下跑通【DTS部分】

20240112让移远mini-PCIE接口的4G模块EC20在Firefly的AIO-3399J开发板的Android11下跑通【DTS部分】 2024/1/12 16:20 https://blog.csdn.net/u010164190/article/details/79096345 [Android6.0][RK3399] PCIe 接口 4G模块 EC20 调试记录 https://blog.csdn.net/hnjztyx/artic…

vue3+ts+vite+elementPlus后台管理系统学习总结01

vue3tsviteelementPlus后台管理系统学习总结01 一&#xff1a;运行源代码一&#xff1a;按照博客一步步操作1.ts中引入path模块出错&#xff1a;Cannot find module path or its corresponding type declarations.2.安装最新版本的pnpm:3.配置自动导入时&#xff0c;遇到.eslin…