流处理简介

article/2025/9/14 12:23:35

一. 流式处理简介

 

在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得简洁了许多,通常我们需要多行代码才能完成的操作,借助于流式处理可以在一行中实现。比如我们希望对一个包含整数的集合中筛选出所有的偶数,并将其封装成为一个新的List返回,那么在java8之前,我们需要通过如下代码实现:

 
  1. List<Integer> evens = new ArrayList<>();

  2. for (final Integer num : nums) {

  3. if (num % 2 == 0) {

  4. evens.add(num);

  5. }

  6. }

通过java8的流式处理,我们可以将代码简化为:

List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());

先简单解释一下上面这行语句的含义,stream()操作将集合转换成一个流,filter()执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect()对结果进行封装处理,并通过Collectors.toList()指定其封装成为一个List集合返回。

由上面的例子可以看出,java8的流式处理极大的简化了对于集合的操作,实际上不光是集合,包括数组、文件等,只要是可以转换成流,我们都可以借助流式处理,类似于我们写SQL语句一样对其进行操作。java8通过内部迭代来实现对流的处理,一个流式处理可以分为三个部分:转换成流、中间操作、终端操作。如下图:

以集合为例,一个流式处理的操作我们首先需要调用stream()函数将其转换成流,然后再调用相应的中间操作达到我们需要对集合进行的操作,比如筛选、转换等,最后通过终端操作对前面的结果进行封装,返回我们需要的形式。

二. 中间操作

我们定义一个简单的学生实体类,用于后面的例子演示:

复制代码

 
  1. public class Student {

  2.  
  3. /** 学号 */

  4. private long id;

  5.  
  6. private String name;

  7.  
  8. private int age;

  9.  
  10. /** 年级 */

  11. private int grade;

  12.  
  13. /** 专业 */

  14. private String major;

  15.  
  16. /** 学校 */

  17. private String school;

  18.  
  19. // 省略getter和setter

  20. }

复制代码

复制代码

 
  1. // 初始化

  2. List<Student> students = new ArrayList<Student>() {

  3. {

  4. add(new Student(20160001, "孔明", 20, 1, "土木工程", "武汉大学"));

  5. add(new Student(20160002, "伯约", 21, 2, "信息安全", "武汉大学"));

  6. add(new Student(20160003, "玄德", 22, 3, "经济管理", "武汉大学"));

  7. add(new Student(20160004, "云长", 21, 2, "信息安全", "武汉大学"));

  8. add(new Student(20161001, "翼德", 21, 2, "机械与自动化", "华中科技大学"));

  9. add(new Student(20161002, "元直", 23, 4, "土木工程", "华中科技大学"));

  10. add(new Student(20161003, "奉孝", 23, 4, "计算机科学", "华中科技大学"));

  11. add(new Student(20162001, "仲谋", 22, 3, "土木工程", "浙江大学"));

  12. add(new Student(20162002, "鲁肃", 23, 4, "计算机科学", "浙江大学"));

  13. add(new Student(20163001, "丁奉", 24, 5, "土木工程", "南京大学"));

  14. }

  15. };

复制代码

2.1 过滤

过滤,顾名思义就是按照给定的要求对集合进行筛选满足条件的元素,java8提供的筛选操作包括:filter、distinct、limit、skip。

filter
在前面的例子中我们已经演示了如何使用filter,其定义为:Stream<T> filter(Predicate<? super T> predicate),filter接受一个谓词Predicate,我们可以通过这个谓词定义筛选条件,在介绍lambda表达式时我们介绍过Predicate是一个函数式接口,其包含一个test(T t)方法,该方法返回boolean。现在我们希望从集合students中筛选出所有武汉大学的学生,那么我们可以通过filter来实现,并将筛选操作作为参数传递给filter:

 
  1. List<Student> whuStudents = students.stream()

  2. .filter(student -> "武汉大学".equals(student.getSchool()))

  3.  
  4. .collect(Collectors.toList());

distinct
distinct操作类似于我们在写SQL语句时,添加的DISTINCT关键字,用于去重处理,distinct基于Object.equals(Object)实现,回到最开始的例子,假设我们希望筛选出所有不重复的偶数,那么可以添加distinct操作:

 
  1. List<Integer> evens = nums.stream()

  2. .filter(num -> num % 2 == 0).distinct()

  3. .collect(Collectors.toList());

limit
limit操作也类似于SQL语句中的LIMIT关键字,不过相对功能较弱,limit返回包含前n个元素的流,当集合大小小于n时,则返回实际长度,比如下面的例子返回前两个专业为土木工程专业的学生:

 
  1. List<Student> civilStudents = students.stream()

  2. .filter(student -> "土木工程".equals(student.getMajor())).limit(2)

  3. .collect(Collectors.toList());

说到limit,不得不提及一下另外一个流操作:sorted。该操作用于对流中元素进行排序,sorted要求待比较的元素必须实现Comparable接口,如果没有实现也不要紧,我们可以将比较器作为参数传递给sorted(Comparator<? super T> comparator),比如我们希望筛选出专业为土木工程的学生,并按年龄从小到大排序,筛选出年龄最小的两个学生,那么可以实现为:

 
  1. List<Student> sortedCivilStudents = students.stream()

  2. .filter(student -> "土木工程".equals(student.getMajor())).sorted((s1, s2) -> s1.getAge() - s2.getAge())

  3. .limit(2)

  4. .collect(Collectors.toList());

skip
skip操作与limit操作相反,如同其字面意思一样,是跳过前n个元素,比如我们希望找出排序在2之后的土木工程专业的学生,那么可以实现为:

 
  1. List<Student> civilStudents = students.stream()

  2. .filter(student -> "土木工程".equals(student.getMajor()))

  3. .skip(2)

  4. .collect(Collectors.toList());

通过skip,就会跳过前面两个元素,返回由后面所有元素构造的流,如果n大于满足条件的集合的长度,则会返回一个空的集合。

2.2 映射

在SQL中,借助SELECT关键字后面添加需要的字段名称,可以仅输出我们需要的字段数据,而流式处理的映射操作也是实现这一目的,在java8的流式处理中,主要包含两类映射操作:map和flatMap。

map
举例说明,假设我们希望筛选出所有专业为计算机科学的学生姓名,那么我们可以在filter筛选的基础之上,通过map将学生实体映射成为学生姓名字符串,具体实现如下:

 
  1. List<String> names = students.stream()

  2. .filter(student -> "计算机科学".equals(student.getMajor()))

  3. .map(Student::getName).collect(Collectors.toList());

除了上面这类基础的map,java8还提供了mapToDouble(ToDoubleFunction<? super T> mapper)mapToInt(ToIntFunction<? super T> mapper)mapToLong(ToLongFunction<? super T> mapper),这些映射分别返回对应类型的流,java8为这些流设定了一些特殊的操作,比如我们希望计算所有专业为计算机科学学生的年龄之和,那么我们可以实现如下:

 
  1. int totalAge = students.stream()

  2. .filter(student -> "计算机科学".equals(student.getMajor()))

  3. .mapToInt(Student::getAge).sum();

通过将Student按照年龄直接映射为IntStream,我们可以直接调用提供的sum()方法来达到目的,此外使用这些数值流的好处还在于可以避免jvm装箱操作所带来的性能消耗。

flatMap
flatMap与map的区别在于 flatMap是将一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流 。举例说明,假设我们有一个字符串数组String[] strs = {"java8", "is", "easy", "to", "use"};,我们希望输出构成这一数组的所有非重复字符,那么我们可能首先会想到如下实现:

 
  1. List<String[]> distinctStrs = Arrays.stream(strs)

  2. .map(str -> str.split("")) // 映射成为Stream<String[]>

  3. .distinct()

  4. .collect(Collectors.toList());

在执行map操作以后,我们得到是一个包含多个字符串(构成一个字符串的字符数组)的流,此时执行distinct操作是基于在这些字符串数组之间的对比,所以达不到我们希望的目的,此时的输出为:

 
  1. [j, a, v, a, 8]

  2. [i, s]

  3. [e, a, s, y]

  4. [t, o]

  5. [u, s, e]

distinct只有对于一个包含多个字符的流进行操作才能达到我们的目的,即对Stream<String>进行操作。此时flatMap就可以达到我们的目的:

 
  1. List<String> distinctStrs = Arrays.stream(strs)

  2. .map(str -> str.split("")) // 映射成为Stream<String[]>

  3. .flatMap(Arrays::stream) // 扁平化为Stream<String>

  4. .distinct()

  5. .collect(Collectors.toList());

flatMap将由map映射得到的Stream<String[]>,转换成由各个字符串数组映射成的流Stream<String>,再将这些小的流扁平化成为一个由所有字符串构成的大流Steam<String>,从而能够达到我们的目的。
与map类似,flatMap也提供了针对特定类型的映射操作:flatMapToDouble(Function<? super T,? extends DoubleStream> mapper)flatMapToInt(Function<? super T,? extends IntStream> mapper)flatMapToLong(Function<? super T,? extends LongStream> mapper)

三. 终端操作

终端操作是流式处理的最后一步,我们可以在终端操作中实现对流查找、归约等操作。

3.1 查找

allMatch
allMatch用于检测是否全部都满足指定的参数行为,如果全部满足则返回true,例如我们希望检测是否所有的学生都已满18周岁,那么可以实现为:

boolean isAdult = students.stream().allMatch(student -> student.getAge() >= 18);

anyMatch
anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true,例如我们希望检测是否有来自武汉大学的学生,那么可以实现为:

boolean hasWhu = students.stream().anyMatch(student -> "武汉大学".equals(student.getSchool()));

noneMathch
noneMatch用于检测是否不存在满足指定行为的元素,如果不存在则返回true,例如我们希望检测是否不存在专业为计算机科学的学生,可以实现如下:

boolean noneCs = students.stream().noneMatch(student -> "计算机科学".equals(student.getMajor()));

findFirst
findFirst用于返回满足条件的第一个元素,比如我们希望选出专业为土木工程的排在第一个学生,那么可以实现如下:

Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findFirst();

findFirst不携带参数,具体的查找条件可以通过filter设置,此外我们可以发现findFirst返回的是一个Optional类型,关于该类型的具体讲解可以参考上一篇:Java8新特性 – Optional类。

findAny
findAny相对于findFirst的区别在于,findAny不一定返回第一个,而是返回任意一个,比如我们希望返回任意一个专业为土木工程的学生,可以实现如下:

Optional<Student> optStu = students.stream().filter(student -> "土木工程".equals(student.getMajor())).findAny();

实际上对于顺序流式处理而言,findFirst和findAny返回的结果是一样的,至于为什么会这样设计,是因为在下一篇我们介绍的并行流式处理,当我们启用并行流式处理的时候,查找第一个元素往往会有很多限制,如果不是特别需求,在并行流式处理中使用findAny的性能要比findFirst好。

3.2 归约

前面的例子中我们大部分都是通过collect(Collectors.toList())对数据封装返回,如我的目标不是返回一个新的集合,而是希望对经过参数化操作后的集合进行进一步的运算,那么我们可用对集合实施归约操作。java8的流式处理提供了reduce方法来达到这一目的。

前面我们通过mapToInt将Stream<Student>映射成为IntStream,并通过IntStream的sum方法求得所有学生的年龄之和,实际上我们通过归约操作,也可以达到这一目的,实现如下:

复制代码

 
  1. // 前面例子中的方法

  2. int totalAge = students.stream()

  3. .filter(student -> "计算机科学".equals(student.getMajor()))

  4. .mapToInt(Student::getAge).sum();

  5. // 归约操作

  6. int totalAge = students.stream()

  7. .filter(student -> "计算机科学".equals(student.getMajor()))

  8. .map(Student::getAge)

  9. .reduce(0, (a, b) -> a + b);

  10.  
  11. // 进一步简化

  12. int totalAge2 = students.stream()

  13. .filter(student -> "计算机科学".equals(student.getMajor()))

  14. .map(Student::getAge)

  15. .reduce(0, Integer::sum);

  16.  
  17. // 采用无初始值的重载版本,需要注意返回Optional

  18. Optional<Integer> totalAge = students.stream()

  19. .filter(student -> "计算机科学".equals(student.getMajor()))

  20. .map(Student::getAge)

  21. .reduce(Integer::sum); // 去掉初始值

复制代码

3.3 收集

前面利用collect(Collectors.toList())是一个简单的收集操作,是对处理结果的封装,对应的还有toSettoMap,以满足我们对于结果组织的需求。这些方法均来自于java.util.stream.Collectors,我们可以称之为收集器。

3.3.1 归约

收集器也提供了相应的归约操作,但是与reduce在内部实现上是有区别的,收集器更加适用于可变容器上的归约操作,这些收集器广义上均基于Collectors.reducing()实现。

例1:求学生的总人数

 
  1. long count = students.stream().collect(Collectors.counting());

  2.  
  3. // 进一步简化

  4. long count = students.stream().count();

例2:求年龄的最大值和最小值

复制代码

 
  1. // 求最大年龄

  2. Optional<Student> olderStudent = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()));

  3.  
  4. // 进一步简化

  5. Optional<Student> olderStudent2 = students.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));

  6.  
  7. // 求最小年龄

  8. Optional<Student> olderStudent3 = students.stream().collect(Collectors.minBy(Comparator.comparing(Student::getAge)));

复制代码

例3:求年龄总和

int totalAge4 = students.stream().collect(Collectors.summingInt(Student::getAge));

对应的还有summingLongsummingDouble

例4:求年龄的平均值

double avgAge = students.stream().collect(Collectors.averagingInt(Student::getAge));

对应的还有averagingLongaveragingDouble

例5:一次性得到元素个数、总和、均值、最大值、最小值

IntSummaryStatistics statistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));

输出:

IntSummaryStatistics{count=10, sum=220, min=20, average=22.000000, max=24}

对应的还有summarizingLongsummarizingDouble

例6:字符串拼接

 
  1. String names = students.stream().map(Student::getName).collect(Collectors.joining());

  2. // 输出:孔明伯约玄德云长翼德元直奉孝仲谋鲁肃丁奉

  3. String names = students.stream().map(Student::getName).collect(Collectors.joining(", "));

  4. // 输出:孔明, 伯约, 玄德, 云长, 翼德, 元直, 奉孝, 仲谋, 鲁肃, 丁奉

3.3.2 分组

在数据库操作中,我们可以通过GROUP BY关键字对查询到的数据进行分组,java8的流式处理也为我们提供了这样的功能Collectors.groupingBy来操作集合。比如我们可以按学校对上面的学生进行分组:

Map<String, List<Student>> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool));

groupingBy接收一个分类器Function<? super T, ? extends K> classifier,我们可以自定义分类器来实现需要的分类效果。

上面演示的是一级分组,我们还可以定义多个分类器实现 多级分组,比如我们希望在按学校分组的基础之上再按照专业进行分组,实现如下:

 
  1. Map<String, Map<String, List<Student>>> groups2 = students.stream().collect(

  2. Collectors.groupingBy(Student::getSchool, // 一级分组,按学校

  3. Collectors.groupingBy(Student::getMajor))); // 二级分组,按专业

实际上在groupingBy的第二个参数不是只能传递groupingBy,还可以传递任意Collector类型,比如我们可以传递一个Collector.counting,用以统计每个组的个数:

Map<String, Long> groups = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));

如果我们不添加第二个参数,则编译器会默认帮我们添加一个Collectors.toList()

3.3.3 分区

分区可以看做是分组的一种特殊情况,在分区中key只有两种情况:true或false,目的是将待分区集合按照条件一分为二,java8的流式处理利用ollectors.partitioningBy()方法实现分区,该方法接收一个谓词,例如我们希望将学生分为武大学生和非武大学生,那么可以实现如下:

Map<Boolean, List<Student>> partition = students.stream().collect(Collectors.partitioningBy(student -> "武汉大学".equals(student.getSchool())));

分区相对分组的优势在于,我们可以同时得到两类结果,在一些应用场景下可以一步得到我们需要的所有结果,比如将数组分为奇数和偶数。

以上介绍的所有收集器均实现自接口java.util.stream.Collector,该接口的定义如下:

复制代码

 
  1. public interface Collector<T, A, R> {

  2. /**

  3. * A function that creates and returns a new mutable result container.

  4. *

  5. * @return a function which returns a new, mutable result container

  6. */

  7. Supplier<A> supplier();

  8.  
  9. /**

  10. * A function that folds a value into a mutable result container.

  11. *

  12. * @return a function which folds a value into a mutable result container

  13. */

  14. BiConsumer<A, T> accumulator();

  15.  
  16. /**

  17. * A function that accepts two partial results and merges them. The

  18. * combiner function may fold state from one argument into the other and

  19. * return that, or may return a new result container.

  20. *

  21. * @return a function which combines two partial results into a combined

  22. * result

  23. */

  24. BinaryOperator<A> combiner();

  25.  
  26. /**

  27. * Perform the final transformation from the intermediate accumulation type

  28. * {@code A} to the final result type {@code R}.

  29. *

  30. * <p>If the characteristic {@code IDENTITY_TRANSFORM} is

  31. * set, this function may be presumed to be an identity transform with an

  32. * unchecked cast from {@code A} to {@code R}.

  33. *

  34. * @return a function which transforms the intermediate result to the final

  35. * result

  36. */

  37. Function<A, R> finisher();

  38.  
  39. /**

  40. * Returns a {@code Set} of {@code Collector.Characteristics} indicating

  41. * the characteristics of this Collector. This set should be immutable.

  42. *

  43. * @return an immutable set of collector characteristics

  44. */

  45. Set<Characteristics> characteristics();

  46.  
  47. }

复制代码

我们也可以实现该接口来定义自己的收集器,此处不再展开。

四. 并行流式数据处理

流式处理中的很多都适合采用 分而治之 的思想,从而在处理集合较大时,极大的提高代码的性能,java8的设计者也看到了这一点,所以提供了 并行流式处理。上面的例子中我们都是调用stream()方法来启动流式处理,java8还提供了parallelStream()来启动并行流式处理,parallelStream()本质上基于java7的Fork-Join框架实现,其默认的线程数为宿主机的内核数。

启动并行流式处理虽然简单,只需要将stream()替换成parallelStream()即可,但既然是并行,就会涉及到多线程安全问题,所以在启用之前要先确认并行是否值得(并行的效率不一定高于顺序执行),另外就是要保证线程安全。此两项无法保证,那么并行毫无意义,毕竟结果比速度更加重要,以后有时间再来详细分析一下并行流式数据处理的具体实现和最佳实践。


http://chatgpt.dhexx.cn/article/fgnLlyMj.shtml

相关文章

【节点流和处理流】

节点流和处理流 基本介绍 节点流可以从特定数据源读取数据&#xff0c;如FileReader、FileWriter处理流&#xff1a;是对一个已存在的流的连接和封装&#xff0c;通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一…

流数据处理

流数据处理strom 在2011年Storm开源之前&#xff0c;由于Hadoop的火红&#xff0c;整个业界都在喋喋不休地谈论大数据。Hadoop的高吞吐&#xff0c;海量数据处理的能力使得人们可以方便地处理海量数据。但是&#xff0c;Hadoop的缺点也和它的优点同样鲜明——延迟大&#xff0…

一. 流式处理简介

https://www.cnblogs.com/shenlanzhizun/p/6027042.html Java技术学习 https://www.itkc8.com 一. 流式处理简介 在我接触到java8流式处理的时候&#xff0c;我的第一感觉是流式处理让集合操作变得简洁了许多&#xff0c;通常我们需要多行代码才能完成的操作&#xff0c;借助…

Kafka基础-流处理

1. 什么是流处理&#xff1f; 首先&#xff0c;让我们说一下什么是数据流&#xff08;也称为事件流&#xff09;&#xff1f;它是无边界数据集的抽象说法&#xff0c;无边界意味着无限且不断增长&#xff0c;因为随着时间的推移&#xff0c;新数据会不断地到来。 除了无边界的…

流处理基本介绍

1. 什么是流处理 一种被设计来处理无穷数据集的数据处理系统引擎 2. 流处理的几个概念 1. 无穷数据&#xff08;Unbounded data&#xff09;&#xff1a;一种持续生成&#xff0c;本质上是无穷尽的数据集。它经常会被称为“流数据”。然而&#xff0c;用流和批次来定义…

Spark Streaming与流处理

一、流处理 1.1 静态数据处理 在流处理之前&#xff0c;数据通常存储在数据库&#xff0c;文件系统或其他形式的存储系统中。应用程序根据需要查询数据或计算数据。这就是传统的静态数据处理架构。Hadoop 采用 HDFS 进行数据存储&#xff0c;采用 MapReduce 进行数据查询或分…

什么是流处理

流处理正变得像数据处理一样流行。流处理已经超出了其原来的实时数据处理的范畴&#xff0c;它正在成为一种提供数据处理&#xff08;包括批处理&#xff09;&#xff0c;实时应用乃至分布式事务的新方法的技术。 1、什么是流处理&#xff1f; 流处理是不断合并新数据以计算结果…

嵌入式软件升级方法

一、U盘升级 1.在u盘根目录新建文件夹&#xff0c;命名为‘upgrade’ 2.将软件复制到upgrade文件夹中 3.将u盘插到嵌入式服务器usb口上&#xff0c;断电重启服务器 二、PC工具升级 1.打开PC工具&#xff0c;选中要升级的机器&#xff0c;点击‘素材管理’选项卡&#xff0c…

嵌入式软件架构设计之分层设计

关注、星标公众号&#xff0c;不错过精彩内容 整理&#xff1a;黄工 素材来源&#xff1a;网络 参考来源&#xff1a; https://blog.51cto.com/kenotu/1614390 在正规的项目开发中&#xff0c;项目往往是并行开发的&#xff0c;也就是说硬件设计、底层软件设计、应用软件设计等…

嵌入式系统软件层次结构

文章目录 嵌入式系统软件嵌入式系统软件的层次结构硬件抽象层 嵌入式操作系统嵌入式操作系统——WinCE嵌入式操作系统——VxWorks嵌入式操作系统——Linux嵌入式Linux OS的特点 嵌入式操作系统——uCOS嵌入式操作系统—— PalmOS其他嵌入式操作系统华为鸿蒙系统 嵌入式系统软件…

嵌入式软件开发必备知识体系

嵌入式软件开发学习路线 前言 本章节主要介绍嵌入式软件开发概念以及大致的学习知识点的范围 一、嵌入式软件是什么&#xff1f; 百度百科&#xff1a;嵌入式工程师是指具有C/C语言、汇编语言等基础&#xff0c;熟悉模拟电子技术等硬件知识&#xff0c;了解处理器体系结构&a…

嵌入式开发 | 嵌入式开发设计文档该怎么写?

关注星标公众号&#xff0c;不错过精彩内容 作者 | strongerHuang 微信公众号 | 嵌入式专栏 俗话说&#xff0c;不会写文档的工程师不是好的工程师&#xff01; 如果你只会写代码&#xff0c;而从不写文档&#xff0c;迟早有一天会“出事”。这不是危言耸听&#xff0c;现实生活…

简单嵌入式系统软件架构

本文为原创&#xff0c;以下链接有比较及时的更新&#xff1a; https://www.yuque.com/docs/share/334f4a3d-2974-49db-8f68-4db6601a0d21?# 《简单嵌入式系统》 引言 本文描述的内容&#xff0c;适用范围是简单嵌入式系统。举一些可能不恰当的例子&#xff0c;如手环、蓝牙…

嵌入式软件设计层级划分概念

嵌入式软件设计层级划分概念 设计过程中体会的细化更新部分&#xff1a; 层级描述备注应用层直接控制应用&#xff0c;比如led_light_on(),led_light_off() 器件层&#xff08;如果操作复杂可进一步划分为器件应用层和器件驱动层&#xff09;比如&#xff1a;实现led_light_on …

浅议嵌入式软件测试

近年来&#xff0c;随着嵌入式系统的功能和复杂性不断增加&#xff0c;其开发时间和成本也随之不断上升。对于安全关键领域的嵌入式系统和软件来说&#xff0c;其稳定性和可靠性往往需要通过大量的测试和验证来保证。 01.一般软件测试vs嵌入式软件测试 嵌入式软件测试针对嵌入…

嵌入式程序设计思路

项目做的多了&#xff0c;深切地体会到架构的重要性。 俗话说&#xff0c;没有好的架构&#xff0c;移植和复用是件很痛苦的事&#xff0c;只能重复的造轮子。特别是嵌入式的代码&#xff0c;如果应用层中间穿插着驱动层的代码&#xff0c;维护起来是一件相当痛苦的事情。 这…

嵌入式应用软件架构设计

要做到嵌入式应用的代码逻辑清晰&#xff0c;且避免重复的造轮子&#xff0c;没有好的应用架构怎么行。 如果没有好的架构&#xff0c;移植将会是一件很痛苦的事情。 如果没有好的架构&#xff0c;复用是最大的难题&#xff0c;没法更大限度的复用原有的代码。 如果没有好的架…

嵌入式软件设计(1)--概述

嵌入式软件的定义及特点 嵌入式系统的定义 嵌入式系统是以应用为中心&#xff0c;以计算机技术为基础&#xff0c;软硬件可裁剪、功能、可靠性、功耗严格要求的专用计算机系统。 其中要说明的几个点是 1. 嵌入式系统是专用系统&#xff0c;而不是通用系统&#xff0c;其往往…

嵌入式软件设计必看书籍

提高C语言编程能力 以上4本书籍可以提高C语言编程能力&#xff0c;深入理解C语言指针用法&#xff0c;深入理解C语言标准。 提高软件架构设计能力 以上2本书籍掌握以下知识&#xff1a; 1、软件设计原则。 2、软件设计模式。 3、软件设计构架。 4、软件设计思维。 提升对RTO…

嵌入式软件架构的设计

嵌入式软件架构的设计 大多数嵌入式程序员学习编程&#xff0c;都是从开发板的附带例程开始。之后工作也会继续参考那些例程&#xff0c;很多编程习惯、方式也会受之影响。 其实开发板式的编程方式与工作中实际需求的并不完全一致。 开发板的通常卖给初学者&#xff0c;注重…