当前位置: 首页 > news >正文

Java基础:Stream流常用方法

获取Stream流的方式
java.util.stream.Stream 是Java 8新加入的流接口。(并不是一个函数式接口)
获取一个流非常简单,有以下几种常用的方式:

  • 所有 Collection 集合都可通过 stream 默认方法获取流(顺序流)
  • 所有 Collection 集合都可通过parallelStream获取并行流
  • Stream 接口的静态方法 of 可以获取数组对应的流。
  • Arrays的静态方法stream也可以获取流

根据Collection获取流

public static void main(String[] args) {List<String> list = new ArrayList<>();Stream<String> stream1 = list.stream();Set<String> set = new HashSet<>();Stream<String> stream2 = set.stream();Vector<String> vector = new Vector<>();// ...
}   

根据Map获取流

public static void main(String[] args) {Map<String, String> map = new HashMap<>();Stream<String> keyStream = map.keySet().stream();Stream<String> valueStream = map.values().stream();Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}

根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法of ,使用很简单:

public static void main(String[] args) {//使用 Stream.ofString[] array = { "张无忌", "张翠山", "张三丰", "张一元" };Stream<String> stream = Stream.of(array);//使用Arrays的静态方法Arrays.stream(array)
}

Stream流中的常用方法

方法可分成两种:

  • 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除终结方法外,其余方法均为延迟方法)
  • 终结方法:返回值类型不是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。本节中,终结方法包括 count 和 forEach 方法。

1.forEach(终结方法)

用于遍历的方法,参数传入一个函数式接口:Consumer

public static void main(String[] args) {Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");stream.forEach(name‐> System.out.println(name));
}

2.过滤:filter

可用于过滤。可通过 filter 方法将一个流转换成另一个子集流。

public static void main(String[] args) {//创建一个流Stream<String> stream = Stream.of( "刘德华", "张国荣", "彭于晏", "纳什", "吴彦祖");//对流中元素过滤,只要姓张的人Stream<String> stream2 = stream.filter(name -> {return name.startsWith("张");});//遍历过滤后的流stream2.forEach(name -> System.out.println(name));
}

3.映射(转换):map

如果需要将流中的元素映射到另一个流中,可用 map 方法。

该接口需要一个 Function 函数式接口参数

/*** stream流的map方法练习* map方法可以将流中的元素映射到另一个流中* map方法的参数是一个Function函数式接口*/@Testpublic void test(){//创建一个流,里面是字符串类型的整数Stream<String> stream1 = Stream.of("2", "32", "2", "33", "2");//把stream1流中的整数全部转成int类型Stream<Integer> stream2 = stream1.map((String s) -> {return Integer.parseInt(s);});//遍历stream2.forEach((i)-> System.out.println(i));}

4.统计个数:count(终结方法)

正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:

该方法返回一个long值代表元素个数(不像旧集合那样是int值)。

public class Demo09StreamCount {public static void main(String[] args) {Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");//筛选姓张的Stream<String> result = original.filter(s ‐> s.startsWith("张"));//输出个数System.out.println(result.count()); // 2}
}

5.取用前几个(截取):limit

limit 方法可对流进行截取,只取用前n个。

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。

public class Demo10StreamLimit {public static void main(String[] args) {Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");//截取前两个Stream<String> result = original.limit(2);System.out.println(result.count()); // 2}
}

6.跳过前几个元素:skip

如果希望跳过前几个元素,可用 skip 方法获取一个截取后的新流:

如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。

public class Demo11StreamSkip {public static void main(String[] args) {Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");//跳过前两个,返回一个新的流Stream<String> result = original.skip(2);System.out.println(result.count()); // 1}
}

7.组合(合并流):concat

如果有两个流,希望合并为一个流,可以用 Stream 接口的静态方法 concat :

public class Demo12StreamConcat {public static void main(String[] args) {Stream<String> streamA = Stream.of("张无忌");Stream<String> streamB = Stream.of("张翠山");//合并成一个新的流Stream<String> result = Stream.concat(streamA, streamB);}
}

8.筛选:distinct

去除流中重复元素(使用hashcode和equals方法来对比)

9.映射(打开后再转换):flatMap

内部传入一个Function函数式接口,跟map的区别就是这个会把流中的元素打开,再组合成一个新的流

// map和flatMap的练习
public class StreamDemo {@Testpublic void test(){List<String> list = Arrays.asList("aa","bb","cc","dd");// 练习1 (map) 输出的全是大写list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);System.out.println("----------");// 练习2(map)流里还有流,需要两个遍历才行看到里面内容Stream<Stream<Character>> streamStream = list.stream().map(StreamDemo::fromStringToStream);streamStream.forEach(s -> {s.forEach(System.out::println);});System.out.println("---------");// 练习3(flatMap)流里还有流,使用flatMap可以直接把里面的流打开,一次遍历就可以了Stream<Character> characterStream = list.stream().flatMap(StreamDemo::fromStringToStream);characterStream.forEach(System.out::println);}/***  将字符串中的多个字符构成的集合转换为对应的stream* @param str* @return*/public static Stream<Character> fromStringToStream(String str){ArrayList<Character> list = new ArrayList();// 将字符串转成字符数组,并遍历加入list集合for(Character c : str.toCharArray()){list.add(c);}// 返回list集合的stream流return list.stream();}
}

10.自然排序:sorted

看下一条里的代码

11.定制排序:sorted(Comparator com)

 /*** 排序的练习*/@Testpublic void test2(){List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);// 按照自然排序integers.stream().sorted().forEach(System.out::println);System.out.println("---------");List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);// 定制排序(大到小),需要传入Comparator接口(如果流中的是引用类型,只能用定制排序)// 简写:integers2.stream().sorted((e1,e2) -> e2-e1).forEach(System.out::println);integers2.stream().sorted((e1,e2) -> {return e2-e1;}).forEach(System.out::println);}

12.检测匹配(终结方法):

返回一个Boolean值

是否全部匹配:allMatch

是否至少匹配一个:anyMatch

是否没有匹配的:noneMatch

 /*** 匹配的练习*/@Testpublic void test3(){List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);// 判断是否全部大于5boolean b = integers.stream().allMatch(i -> i > 5);// 结束输出falseSystem.out.println(b);System.out.println("-------");List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);// 检测是否匹配至少一个元素boolean b1 = integers2.stream().anyMatch(i -> i > 5);// 输出trueSystem.out.println(b1);System.out.println("-------");List<Integer> integers3 = List.of(124, 2, 15, 12, 51, -5, 5);// 检查是否没有匹配的元素boolean b2 = integers3.stream().noneMatch(i -> i > 1000);// 输出true,全部不匹配System.out.println(b2);}

13.查找元素(终结方法)

查找第一个元素:findFirst,返回Optional类型

查找其中一个元素:findAny,返回Optional类型

public void test4(){List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);// 输出第一个元素Optional<Integer> first = integers.stream().findFirst();// 输出结果是Optional[124]System.out.println(first);System.out.println("-------------");List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);// 返回其中一个元素Optional<Integer> any = integers2.stream().findAny();System.out.println(any);}

14.查找最大最小值(终结方法)

max(comparator c)

min(comparator c)

 /*** 查找最大最小值*/@Testpublic void test5(){List<Person> list = new ArrayList<>();list.add(new Person("马化腾",25,3000));list.add(new Person("李彦宏",27,2545));list.add(new Person("雷军",35,4515));list.add(new Person("马云",55,9877));//  查找年龄最大的人Optional<Person> max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());// 返回马云,55岁年龄最大System.out.println(max.get());System.out.println("--------");

15.规约(终结方法)

reduce(T identity ,BinaryOperator) 第一个参数是初始值,第二个参数是一个函数式接口

reduce(BinaryOperator) 参数是一个函数式接口

/*** 归约的练习*/@Testpublic void test6(){List<Integer> integers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 求集合里数字的和(归约)reduce第一个参数是初始值。Integer sum = integers.stream().reduce(0, Integer::sum);System.out.println(sum);System.out.println("-------");List<Person> list = new ArrayList<>();list.add(new Person("马化腾",25,3000));list.add(new Person("李彦宏",27,2545));list.add(new Person("雷军",35,4515));list.add(new Person("马云",55,9877));// 求所有人的工资和(归约)// 不用方法引用写法:Optional<Integer> reduce = list.stream().map(person -> person.getSalary()).reduce((e1, e2) -> e1 + e2);Optional<Integer> reduce = list.stream().map(Person::getSalary).reduce(Integer::sum);// 输出Optional[19937]System.out.println(reduce);}

16.收集(终结方法)

collect(Collector c):将流转化为其他形式,接收一个Collector接口的实现

/*** 收集的练习*/@Testpublic void test7(){List<Person> list = new ArrayList<>();list.add(new Person("马化腾",25,3000));list.add(new Person("李彦宏",27,2545));list.add(new Person("雷军",35,4515));list.add(new Person("马云",55,9877));// 把年龄大于30岁的人,转成一个list集合List<Person> collect = list.stream().filter(person -> person.getAge() > 30).collect(Collectors.toList());// 遍历输出(输出雷军和马云)for (Person person : collect) {System.out.println(person);}System.out.println("----------");List<Person> list2 = new ArrayList<>();list2.add(new Person("马化腾",25,3000));list2.add(new Person("李彦宏",27,2545));list2.add(new Person("雷军",35,4515));list2.add(new Person("马云",55,9877));// 把姓马的人,转成Set集合Set<Person> set = list2.stream().filter(person -> person.getName().startsWith("马")).collect(Collectors.toSet());// 输出马云和马化腾set.forEach(System.out::println);}

17.迭代:iterate

可以使用Stream.iterate创建流值,即所谓的无限流。

//Stream.iterate(initial value, next value)Stream.iterate(0, n -> n + 1).limit(5).forEach(x -> System.out.println(x));

输出:

0
1
2
3
4   

18.查看:peek

peek接收的是一个Consumer函数,peek 操作会按照 Consumer 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性

@Testpublic void test1(){List<String> collect = Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("查看一下刚过滤出的值:" + e)).map(String::toUpperCase).peek(e -> System.out.println("查看一下转大写之后的值:" + e)).collect(Collectors.toList());System.out.println("-----分割线-----");// 遍历过滤后的集合for (String s : collect) {System.out.println(s);}}

输出:

查看一下刚过滤出的值:three
查看一下转大写之后的值:THREE
查看一下刚过滤出的值:four
查看一下转大写之后的值:FOUR
-----分割线-----
THREE
FOUR

Srtream流中方法的使用练习

/*** 	1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。*	2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。*	3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。*	4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。*	5. 将两个队伍合并为一个队伍;存储到一个新集合中。*	6. 根据姓名创建 Person 对象;存储到一个新集合中。*	7. 打印整个队伍的Person对象信息。*/
public class Demo3 {public static void main(String[] args) {//第一支队伍ArrayList<String> one = new ArrayList<>();one.add("迪丽热巴");one.add("宋远桥");one.add("苏星河");one.add("石破天");one.add("石中玉");one.add("老子");one.add("庄子");one.add("洪七公");//第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。//第一个队伍筛选之后只要前3个人;存储到一个新集合中。Stream<String> stream = one.stream();Stream<String> stream1 = stream.filter(name -> name.length() == 3).limit(3);//第二支队伍ArrayList<String> two = new ArrayList<>();two.add("古力娜扎");two.add("张无忌");two.add("赵丽颖");two.add("张三丰");two.add("尼古拉斯赵四");two.add("张天爱");two.add("张二狗");//第二个队伍只要姓张的成员姓名;存储到一个新集合中。//第二个队伍筛选之后不要前2个人;存储到一个新集合中。Stream<String> stream2 = two.stream();Stream<String> stream3 = stream2.filter(name -> name.startsWith("张")).skip(2);//合并两个队伍Stream<String> concat = Stream.concat(stream1, stream3);//把合并后的队伍根据姓名创建Person对象,并存入新的集合中,然后打印concat.map(name -> new Person(name)).forEach(p -> System.out.println(p));}
}

相关文章:

Java基础:Stream流常用方法

获取Stream流的方式 java.util.stream.Stream 是Java 8新加入的流接口。&#xff08;并不是一个函数式接口&#xff09; 获取一个流非常简单&#xff0c;有以下几种常用的方式&#xff1a; 所有 Collection 集合都可通过 stream 默认方法获取流&#xff08;顺序流&#xff09;…...

ImageNet使用方法(细节)自用!

学习记录&#xff0c;自用。 1. 下载数据集 点击以下链接下载种子文件&#xff0c;然后使用迅雷进行下载&#xff0c;仅下载勾选的文件即可。 https://hyper.ai/datasets/4889/c107755f6de25ba43c190f37dd0168dbd1c0877e 2. 解压 找到下载好的ILSVRC2012_img_train.tar 和…...

C/C++外观模式解析:简化复杂子系统的高效方法

C外观模式揭秘&#xff1a;简化复杂子系统的高效方法 引言设计模式的重要性外观模式简介与应用场景外观模式在现代软件设计中的地位与价值 外观模式基本概念外观模式的定义与核心思想提供简单接口隐藏复杂子系统设计原则与外观模式的关系外观模式实现外观模式的UML图 外观模式的…...

追梦之旅【数据结构篇】——详解小白如何使用C语言实现堆数据结构

详解小白如何使用C语言实现堆数据结构 “痛”撕堆排序~&#x1f60e; 前言&#x1f64c;什么是堆&#xff1f;堆的概念及结构 堆的性质&#xff1a;堆的实现堆向下调整算法画图分析&#xff1a;堆向下调整算法源代码分享&#xff1a;向下调整建小堆向下调整建大堆 堆向上调整算…...

cocoscreator性能优化4-Sprite颜色数据去除

前言 Sprite是游戏内容的一个基本组成元素&#xff0c;包括ui、道具、立绘等各种地方都会用到。大部分情况下美术会帮我们调好图片颜色&#xff0c;我们只要把图片直接放到游戏里就行了。Sprite默认的渲染顶点数据中包含了颜色数据&#xff0c;由于我们并不需要去修改颜色&…...

系统接口幂等性设计探究

前言&#xff1a; 刚开始工作的时候写了一个带UI页面的工具&#xff0c;需要设计登录功能&#xff0c;登录功能也很简单&#xff0c;输入用户名密码点击登录&#xff0c;触发后台查询并比对密码&#xff0c;如果登录成功则返回消息给前端&#xff0c;前端把消息弹出提示一下。…...

C learning_7

目录 1.for循环 1.虽然while循环和for循环本质上都可以实现循环&#xff0c;但是它们在使用方法和场合上还是有一些区别的。 2.while循环中存在循环的三个必须条件&#xff0c;但是由于风格的问题使得三个部分很可能偏离较远&#xff0c;这样 查找修改就不够集中和方便。所以…...

PageRank算法介绍

互联网上有数百亿个网页&#xff0c;可以分为这么几类&#xff1a;不含有用信息的&#xff0c;比如垃圾邮件&#xff1b;少数人比较感兴趣的&#xff0c;但范围不是很广的&#xff0c;比如个人博客、婚礼公告或家庭像册&#xff1b;很多人感兴趣的并且十分有用的&#xff0c;比…...

springboot+vue职称评审管理系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的职称评审管理系统。项目源码请联系风歌&#xff0c;文末附上联系信息 。 目前有各类成品java毕设&#xff0c;需要请看文末联系方式 …...

腾讯云4核8G轻量服务器12M支持多少访客同时在线?并发数怎么算?

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…...

图片英文翻译成中文转换器-中文翻译英文软件

您正在准备一份重要的英文资料或文件&#xff0c;但是您还不是很熟练地掌握英文&#xff0c;需要翻译才能完成您的任务吗&#xff1f;哪个软件能够免费把英文文档翻译成中文&#xff1f;让我们带您了解如何使用我们的翻译软件来免费翻译英文文档为中文。 我们的翻译软件是一款功…...

月薪10k和40k的程序员差距有多大?

程序员的薪资一直是大家关注的焦点&#xff0c;相较于其他行业&#xff0c;程序员的高薪也是有目共睹的&#xff0c;而不同等级的程序员处理问题的方式与他们的薪资直接挂钩。 接下来就一起看一下月薪10k、20k、30k、40k的程序员面对问题都是怎么处理的吧&#xff01; 场景一 …...

gateway整合knife4j(微服务在线文档)

文章目录 knife4j 微服务整合一、微服务与单体项目文档整合的区别二、开始整合1. 搭建一个父子maven模块的微服务,并引入gateway2.开始整合文档 总结 knife4j 微服务整合 由于单个服务的knife4j 整合之前已经写过了,那么由于效果比较好,然后微服务的项目中也想引入,所以开始微…...

ASP.NET 记录 HttpRequest HttpResponse HttpServerUtility

纯属个人记录,会有错误 HttpRequest Browser是获取客户端浏览器的信息 Cookies是获取客户端的Cookies QueryString是获取客户端提交的数据 ServerVariables是获取服务器端或客户端的环境变量信息 Browser 语法格式: Request.Browser[“浏览器特性名”] 常见的特性名 名称说…...

Python 人工智能:11~15

原文&#xff1a;Artificial Intelligence with Python 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心如何…...

辉煌优配|军工板块逆市上涨,16只概念股已披露一季度业绩预喜

今日&#xff0c;军工股逆市上涨。 4月21日&#xff0c;A股三大股指低开低走&#xff0c;半导体、AI使用、信创工业、软件等科技属性概念领跌&#xff0c;国防军工、食品饮料和电力设备等板块上涨。 工业互联网中心工业规模超1.2万亿元 据央视新闻报道&#xff0c;本年是《工业…...

看板与 Scrum:有什么区别?

看板和Scrum是项目管理方法论&#xff0c;以小增量完成项目任务并强调持续改进。但是他们用来实现这些目标的过程是不同的。看板以可视化任务和连续流程为中心&#xff0c;而Scrum更多是关于为每个交付周期实施时间表和分配设定角色。 在看板和Scrum之间做出选择并不总是必要…...

零代码是什么?零代码平台适合谁用?

随着信息技术的发展&#xff0c;软件开发领域也不断发生变革&#xff0c;零代码&#xff08;No-Code&#xff09;开发模式越来越受到关注。 零代码到底是什么&#xff0c;能不能用通俗的话来说&#xff1f;这就来给大家讲一讲&#xff01; 01 零代码为什么出现&#xff1f; 随…...

CNStack 云服务云组件:打造丰富的云原生技术中台生态

作者&#xff1a;刘裕惺 CNStack 相关阅读&#xff1a; CNStack 多集群服务&#xff1a;基于OCM 打造完善的集群管理能力 CNStack 虚拟化服务&#xff1a;实现虚拟机和容器资源的共池管理 CNStack 云边协同平台&#xff1a;实现原生边缘竟能如此简单 01 前言 CNStack 2.0…...

#PythonPytorch 1.如何入门深度学习模型

我之前也写过一篇关于Keras的深度学习入门blog&#xff0c;#Python&Keras 1.如何从无到有在自己的数据集上实现深度学习模型&#xff08;入门&#xff09;&#xff0c;里面也有介绍了一下一点点机器学习的概念和理解深度学习的输入&#xff0c;如果对这方面有疑惑的朋友可以…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...