diff --git a/README.md b/README.md index 7cf58b258f0..786e0ccf6d9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -👉 如果你不知道该学习什么的话,请看 [Java 学习线路图是怎样的?]( https://zhuanlan.zhihu.com/p/379041500) (原创不易,欢迎点赞),这是 2021 最新最完善的 Java 学习路线!另外,我整理了一份各个技术的学习路线,需要的小伙伴[加我微信](#联系我)备注“**Github-学习路线**”即可! +👉 如果你不知道该学习什么的话,请看 [Java 学习线路图是怎样的?]( https://zhuanlan.zhihu.com/p/379041500) (原创不易,欢迎点赞),这是 2021 最新最完善的 Java 学习路线!另外,[我的朋友整理了一份消息队列常见面试题,需要的小伙伴可以点击领取!](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100025985&idx=1&sn=681af486050fabbeea27fa1c3bec5d65&chksm=4ea1e94a79d6605c72f280b5268100c6e96c6ab1dc9a0178b33e25a72ff5f4eac3dcb56fa44f#rd) 👉 推荐 [在线阅读](https://snailclimb.gitee.io/javaguide) (Github 访问速度比较慢可能会导致部分图片无法刷新出来) @@ -44,7 +44,7 @@ ### 基础 -**知识点/面试题:**(必看:+1: ) +**知识点/面试题** : (必看:+1: ) 1. **[Java 基础知识](docs/java/basis/Java基础知识.md)** 2. **[Java 基础知识疑难点/易错点](docs/java/basis/Java基础知识疑难点.md)** @@ -71,11 +71,11 @@ **重要知识点详解:** -2. **线程池**:[Java 线程池学习总结](./docs/java/multi-thread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/multi-thread/拿来即用的线程池最佳实践.md) -4. [ ThreadLocal 关键字解析](docs/java/multi-thread/万字详解ThreadLocal关键字.md) -5. [并发容器总结](docs/java/multi-thread/并发容器总结.md) -6. [JUC 中的 Atomic 原子类总结](docs/java/multi-thread/Atomic原子类总结.md) -7. [AQS 原理以及 AQS 同步组件总结](docs/java/multi-thread/AQS原理以及AQS同步组件总结.md) +1. **线程池**:[Java 线程池学习总结](./docs/java/multi-thread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/multi-thread/拿来即用的线程池最佳实践.md) +2. [ ThreadLocal 关键字解析](docs/java/multi-thread/万字详解ThreadLocal关键字.md) +3. [并发容器总结](docs/java/multi-thread/并发容器总结.md) +4. [JUC 中的 Atomic 原子类总结](docs/java/multi-thread/Atomic原子类总结.md) +5. [AQS 原理以及 AQS 同步组件总结](docs/java/multi-thread/AQS原理以及AQS同步组件总结.md) ### JVM (必看 :+1:) @@ -93,7 +93,12 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle ### 新特性 1. **Java 8** :[Java 8 新特性总结](docs/java/new-features/Java8新特性总结.md)、[Java8常用新特性总结](docs/java/new-features/java8-common-new-features.md) 、[Java 8 学习资源推荐](docs/java/new-features/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/new-features/Java8foreach指南.md) -2. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md) +2. **Java9~Java15** : [一文带你看遍 JDK9~15 的重要新特性!](./docs/java/new-features/java新特性总结.md) + +### 小技巧 + +1. [JAD 反编译](docs/java/tips/JAD反编译tricks.md) +2. [手把手教你定位常见 Java 性能问题](./docs/java/tips/手把手教你定位常见Java性能问题.md) ## 计算机基础 @@ -101,22 +106,27 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle ### 操作系统 -1. [操作系统常见问题总结!](docs/operating-system/basis.md) -2. [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md) -3. [Shell 编程入门](docs/operating-system/Shell.md) +1. [操作系统常见问题总结!](docs/cs-basics/operating-system/basis.md) +2. [后端程序员必备的 Linux 基础知识总结](docs/cs-basics/operating-system/linux.md) +3. [Shell 编程入门](docs/cs-basics/operating-system/Shell.md) ### 网络 -1. [计算机网络常见面试题](docs/network/计算机网络.md) -2. [计算机网络基础知识总结](docs/network/计算机网络知识总结.md) +1. [计算机网络常见面试题](docs/cs-basics/network/计算机网络.md) +2. [计算机网络基础知识总结](docs/cs-basics/network/计算机网络知识总结.md) ### 数据结构 -- **图解数据结构:** - 1. [线性数据结构 :数组、链表、栈、队列](docs/dataStructures-algorithms/data-structure/线性数据结构.md) - 2. [图](docs/dataStructures-algorithms/data-structure/图.md) - 3. [堆](docs/dataStructures-algorithms/data-structure/堆.md) -- [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md) +**图解数据结构:** + +1. [线性数据结构 :数组、链表、栈、队列](docs/cs-basics/data-structure/线性数据结构.md) +2. [图](docs/cs-basics/data-structure/图.md) +3. [堆](docs/cs-basics/data-structure/堆.md) +4. [树](docs/cs-basics/data-structure/树.md) :重点关注[红黑树](docs/cs-basics/data-structure/红黑树.md)、B-,B+,B*树、LSM树 + +其他常用数据结构 : + +1. [布隆过滤器](docs/cs-basics/data-structure/bloom-filter.md) ### 算法 @@ -125,11 +135,13 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle - [算法学习书籍+资源推荐](https://www.zhihu.com/question/323359308/answer/1545320858) 。 - [如何刷Leetcode?](https://www.zhihu.com/question/31092580/answer/1534887374) -**常见算法问题总结:** +**常见算法问题总结** : + +- [几道常见的字符串算法题总结 ](docs/cs-basics/algorithms/几道常见的字符串算法题.md) +- [几道常见的链表算法题总结 ](docs/cs-basics/algorithms/几道常见的链表算法题.md) +- [剑指 offer 部分编程题](docs/cs-basics/algorithms/剑指offer部分编程题.md) -- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的字符串算法题.md) -- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md) -- [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md) +另外,[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-algorithms/) 这个网站总结了常见的算法 ,比较全面系统。 ## 数据库 @@ -147,9 +159,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle 1. [MySQL数据库索引总结](docs/database/mysql/MySQL数据库索引.md) 2. [事务隔离级别(图文详解)](docs/database/mysql/事务隔离级别(图文详解).md) -3. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/InnoDB对MVCC的实现.md) -4. [一条 SQL 语句在 MySQL 中如何执行的](docs/database/mysql/一条sql语句在mysql中如何执行的.md) -5. [关于数据库中如何存储时间的一点思考](docs/database/mysql/关于数据库存储时间的一点思考.md) +3. [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/MySQL三大日志(binlog、redo log和undo log).md) +4. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/InnoDB对MVCC的实现.md) +5. [一条 SQL 语句在 MySQL 中如何执行的](docs/database/mysql/一条sql语句在mysql中如何执行的.md) +6. [关于数据库中如何存储时间的一点思考](docs/database/mysql/关于数据库存储时间的一点思考.md) ### Redis @@ -353,7 +366,6 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读: ## 工具 -1. **Java** :[JAD 反编译](docs/java/JAD反编译tricks.md)、[手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md) 2. **Git** :[Git 入门](docs/tools/Git.md) 3. **Github** : [Github小技巧](docs/tools/Github技巧.md) 4. **Docker** : [Docker 基本概念解读](docs/tools/Docker.md) 、[Docker从入门到上手干事](docs/tools/Docker从入门到实战.md) @@ -377,7 +389,8 @@ Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读: ### 待办 -- [ ] 数据结构总结重构 +- [ ] 计算机网络知识点完善 +- [ ] 分布式常见理论和算法总结完善 ### 捐赠支持 diff --git "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\227\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" "b/docs/cs-basics/algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\227\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" similarity index 100% rename from "docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\227\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" rename to "docs/cs-basics/algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\345\255\227\347\254\246\344\270\262\347\256\227\346\263\225\351\242\230.md" diff --git "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" "b/docs/cs-basics/algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" similarity index 100% rename from "docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" rename to "docs/cs-basics/algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" diff --git "a/docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" "b/docs/cs-basics/algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" similarity index 100% rename from "docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" rename to "docs/cs-basics/algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" diff --git a/docs/dataStructures-algorithms/data-structure/bloom-filter.md b/docs/cs-basics/data-structure/bloom-filter.md similarity index 74% rename from docs/dataStructures-algorithms/data-structure/bloom-filter.md rename to docs/cs-basics/data-structure/bloom-filter.md index bfb7efe7f03..9b5afd6d5c5 100644 --- a/docs/dataStructures-algorithms/data-structure/bloom-filter.md +++ b/docs/cs-basics/data-structure/bloom-filter.md @@ -6,18 +6,18 @@ 2. 布隆过滤器的原理介绍。 3. 布隆过滤器使用场景。 4. 通过 Java 编程手动实现布隆过滤器。 -5. 利用Google开源的Guava中自带的布隆过滤器。 +5. 利用 Google 开源的 Guava 中自带的布隆过滤器。 6. Redis 中的布隆过滤器。 ### 1.什么是布隆过滤器? 首先,我们需要了解布隆过滤器的概念。 -布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于1970年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。 +布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。 ![布隆过滤器示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/布隆过滤器-bit数组.png) -位数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 kb ≈ 122kb 的空间。 +位数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 kb ≈ 122kb 的空间。 总结:**一个名叫 Bloom 的人提出了一种来检索元素是否在给定大集合中的数据结构,这种数据结构是高效且性能很好的,但缺点是具有一定的错误识别率和删除难度。并且,理论情况下,添加到集合中的元素越多,误报的可能性就越大。** @@ -35,11 +35,9 @@ 举个简单的例子: - - ![布隆过滤器hash计算](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/布隆过滤器-hash运算.png) -如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后将对应的位数组的下标设置为 1(当位数组初始化时,所有位置均为0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。 +如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后将对应的位数组的下标设置为 1(当位数组初始化时,所有位置均为 0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。 如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。 @@ -49,7 +47,7 @@ ### 3.布隆过滤器使用场景 -1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。 +1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5 亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。 2. 去重:比如爬给定网址的时候对已经爬取过的 URL 去重。 ### 4.通过 Java 编程手动实现布隆过滤器 @@ -206,7 +204,7 @@ true 实际使用如下: -我们创建了一个最多存放 最多 1500个整数的布隆过滤器,并且我们可以容忍误判的概率为百分之(0.01) +我们创建了一个最多存放 最多 1500 个整数的布隆过滤器,并且我们可以容忍误判的概率为百分之(0.01) ```java // 创建布隆过滤器对象 @@ -224,7 +222,7 @@ System.out.println(filter.mightContain(1)); System.out.println(filter.mightContain(2)); ``` -在我们的示例中,当`mightContain()` 方法返回 *true* 时,我们可以99%确定该元素在过滤器中,当过滤器返回 *false* 时,我们可以100%确定该元素不存在于过滤器中。 +在我们的示例中,当`mightContain()` 方法返回 _true_ 时,我们可以 99%确定该元素在过滤器中,当过滤器返回 _false_ 时,我们可以 100%确定该元素不存在于过滤器中。 **Guava 提供的布隆过滤器的实现还是很不错的(想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用(另外,容量扩展也不容易),而现在互联网一般都是分布式的场景。为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了。** @@ -234,17 +232,17 @@ System.out.println(filter.mightContain(2)); Redis v4.0 之后有了 Module(模块/插件) 功能,Redis Modules 让 Redis 可以使用外部模块扩展其功能 。布隆过滤器就是其中的 Module。详情可以查看 Redis 官方对 Redis Modules 的介绍 :https://redis.io/modules -另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom。其他还有: +另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom。其他还有: - redis-lua-scaling-bloom-filter(lua 脚本实现):https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter -- pyreBloom(Python中的快速Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom +- pyreBloom(Python 中的快速 Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom - ...... RedisBloom 提供了多种语言的客户端支持,包括:Python、Java、JavaScript 和 PHP。 -#### 6.2 使用Docker安装 +#### 6.2 使用 Docker 安装 -如果我们需要体验 Redis 中的布隆过滤器非常简单,通过 Docker 就可以了!我们直接在 Google 搜索 **docker redis bloomfilter** 然后在排除广告的第一条搜素结果就找到了我们想要的答案(这是我平常解决问题的一种方式,分享一下),具体地址:https://hub.docker.com/r/redislabs/rebloom/ (介绍的很详细 )。 +如果我们需要体验 Redis 中的布隆过滤器非常简单,通过 Docker 就可以了!我们直接在 Google 搜索 **docker redis bloomfilter** 然后在排除广告的第一条搜素结果就找到了我们想要的答案(这是我平常解决问题的一种方式,分享一下),具体地址:https://hub.docker.com/r/redislabs/rebloom/ (介绍的很详细 )。 **具体操作如下:** @@ -252,15 +250,15 @@ RedisBloom 提供了多种语言的客户端支持,包括:Python、Java、Ja ➜ ~ docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest ➜ ~ docker exec -it redis-redisbloom bash root@21396d02c252:/data# redis-cli -127.0.0.1:6379> +127.0.0.1:6379> ``` -#### 6.3常用命令一览 +#### 6.3 常用命令一览 -> 注意: key : 布隆过滤器的名称,item : 添加的元素。 +> 注意: key : 布隆过滤器的名称,item : 添加的元素。 -1. **`BF.ADD `**:将元素添加到布隆过滤器中,如果该过滤器尚不存在,则创建该过滤器。格式:`BF.ADD {key} {item}`。 -2. **`BF.MADD `** : 将一个或多个元素添加到“布隆过滤器”中,并创建一个尚不存在的过滤器。该命令的操作方式`BF.ADD`与之相同,只不过它允许多个输入并返回多个值。格式:`BF.MADD {key} {item} [item ...]` 。 +1. **`BF.ADD`**:将元素添加到布隆过滤器中,如果该过滤器尚不存在,则创建该过滤器。格式:`BF.ADD {key} {item}`。 +2. **`BF.MADD`** : 将一个或多个元素添加到“布隆过滤器”中,并创建一个尚不存在的过滤器。该命令的操作方式`BF.ADD`与之相同,只不过它允许多个输入并返回多个值。格式:`BF.MADD {key} {item} [item ...]` 。 3. **`BF.EXISTS` ** : 确定元素是否在布隆过滤器中存在。格式:`BF.EXISTS {key} {item}`。 4. **`BF.MEXISTS`** : 确定一个或者多个元素是否在布隆过滤器中存在格式:`BF.MEXISTS {key} {item} [item ...]`。 @@ -268,19 +266,19 @@ root@21396d02c252:/data# redis-cli 这个命令的格式如下: -`BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] `。 +`BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion]`。 下面简单介绍一下每个参数的具体含义: 1. key:布隆过滤器的名称 -2. error_rate :误报的期望概率。这应该是介于0到1之间的十进制值。例如,对于期望的误报率0.1%(1000中为1),error_rate应该设置为0.001。该数字越接近零,则每个项目的内存消耗越大,并且每个操作的CPU使用率越高。 -3. capacity: 过滤器的容量。当实际存储的元素个数超过这个值之后,性能将开始下降。实际的降级将取决于超出限制的程度。随着过滤器元素数量呈指数增长,性能将线性下降。 +2. error_rate :误报的期望概率。这应该是介于 0 到 1 之间的十进制值。例如,对于期望的误报率 0.1%(1000 中为 1),error_rate 应该设置为 0.001。该数字越接近零,则每个项目的内存消耗越大,并且每个操作的 CPU 使用率越高。 +3. capacity: 过滤器的容量。当实际存储的元素个数超过这个值之后,性能将开始下降。实际的降级将取决于超出限制的程度。随着过滤器元素数量呈指数增长,性能将线性下降。 可选参数: -- expansion:如果创建了一个新的子过滤器,则其大小将是当前过滤器的大小乘以`expansion`。默认扩展值为2。这意味着每个后续子过滤器将是前一个子过滤器的两倍。 +- expansion:如果创建了一个新的子过滤器,则其大小将是当前过滤器的大小乘以`expansion`。默认扩展值为 2。这意味着每个后续子过滤器将是前一个子过滤器的两倍。 -#### 6.4实际使用 +#### 6.4 实际使用 ```shell 127.0.0.1:6379> BF.ADD myFilter java @@ -294,4 +292,3 @@ root@21396d02c252:/data# redis-cli 127.0.0.1:6379> BF.EXISTS myFilter github (integer) 0 ``` - diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\233\276.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\233\276.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\233\276.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\233\276.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\345\271\277\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\227\240\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250\347\232\204\345\211\257\346\234\254.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250\347\232\204\345\211\257\346\234\254.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250\347\232\204\345\211\257\346\234\254.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\347\237\251\351\230\265\345\255\230\345\202\250\347\232\204\345\211\257\346\234\254.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\234\211\345\220\221\345\233\276\347\232\204\351\202\273\346\216\245\350\241\250\345\255\230\345\202\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2421.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2422.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2423.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2424.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2425.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\2426.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" "b/docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" rename to "docs/cs-basics/data-structure/pictures/\345\233\276/\346\267\261\345\272\246\344\274\230\345\205\210\346\220\234\347\264\242\345\233\276\347\244\272.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2401.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2401.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2401.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2401.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2402.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2402.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2402.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2402.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2403.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2403.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2403.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2403.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2404.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2404.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2404.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2404.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2405.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2405.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2405.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2405.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2406.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2406.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2406.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\210\240\351\231\244\345\240\206\351\241\266\345\205\203\347\264\2406.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2401.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2401.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2401.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2401.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2402.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2402.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2402.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2402.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2403.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2403.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2403.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206-\346\217\222\345\205\245\345\205\203\347\264\2403.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\2061.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\2061.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\2061.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\2061.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\2062.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\2062.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\2062.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\2062.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2171.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2171.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2171.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2171.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2172.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2172.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2172.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2172.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2173.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2173.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2173.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2173.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2174.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2174.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2174.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2174.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2175.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2175.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2175.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2175.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2176.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2176.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2176.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\346\216\222\345\272\2176.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\347\232\204\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\347\232\204\345\255\230\345\202\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\240\206\347\232\204\345\255\230\345\202\250.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\240\206\347\232\204\345\255\230\345\202\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2061.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2061.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2061.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2061.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2062.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2062.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2062.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2062.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2063.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2063.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2063.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2063.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2064.png" "b/docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2064.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\345\240\206/\345\273\272\345\240\2064.png" rename to "docs/cs-basics/data-structure/pictures/\345\240\206/\345\273\272\345\240\2064.png" diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.drawio" new file mode 100644 index 00000000000..f8fe81d2c06 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.drawio" @@ -0,0 +1 @@ +7Vtbk6I4FP41eZwukgBJHr1g727tVk1VV+30PNKSVmbQuIit7q/fBAISBLVbFHfa8sHkJORyzpcv5xwR4MFs8xj7i+lfIuARQFawAXgIEILQcuWXkmwzCWMkE0ziMNCddoKn8F+uhZaWrsKAL42OiRBREi5M4VjM53ycGDI/jsXa7PYqInPWhT/he4KnsR/tS7+FQTLNpBSRnfw3Hk6m+czQZVnLzM87650sp34g1iUR9gAexEIkWWm2GfBIKS/XS/bcqKG1WFjM58kpD6yenT8ev/39O3VCf/C2ZpuF7X/Ro7z50UpvGCA3kuP1X9SSk63Wg/vPSq2z/yrmyZdlaqWe7ICcxWbXKEsT9d3Lx5CLecmFWgnFiEiuThpRVvrraZjwp4U/Vi1riSMpmyazSNagLPrLRWbZ13DDA7WIMIoGIhJxOhB+feXueCzlyyQWP3mpJSDsxbKKyd94nPBNo/pgYRSJZi5mPIm3sot+gGBtRw1kzHR9XYKFFk1LiMhlvgbipBh5ZytZ0OZ6h+lQjemqKp4HPXUGZG0u5txUq9x3vH0uV76XK0O1b6uobXUtm4MHe6emoke5DrGKx/zABrA+zn484ckxjO7bpaR3p0bvuSzmkZ+Eb+Zy64yhZ/gqQrmRndktYpidWBV7ZtvUT5WPX3Ug5jwgaiMCkU1sN1+JHtYm1GzF5iyZkvZmSYFT6OTjWMKNNLBc+POPM0GJTDLBoJ4aClk23a/BGJSajGF3zRj2tazc/0RWdq0bs7JzLSsPP5GVHWRa2SEdW9m9lpUfP5GVmXtjVibn+nibMHkulb/nPp0s7xw8Vcn9u8IrLCpX9grt/4NX6FaAQuAHvUKKqIk4hq7q+NFzAVYbRFhH4JLD0irBEh6EZQcAc7oEGKneN+yjYYdDD4UdrNOwg52JvhZR4Z6ICtwlKiiCDy7GDrapdEgQNI3pUPjAIGO27AZtNw9c301J2D2AGIdada1XAkxOs5d3e7xP5Pa49MbcHliXljzX7zl8wdT7Pccusi78HqvelldyfMw8FWQmcCB8INAhiBIHOZhWhj/5zrJqh8nvLFq7hGsxEGpkIEU0LTKQfYSBsukaGEiSQWIeCZNB9JEp040W+VE4mcvqWCKYS3lfUUs49qOebpiFQRA1cVssVvNAMdmwLW5ilfSKtc9Ndg3e0cW4qTlX2jIA0B0AilOcyuXUOQCa06gtA8C9AyD1R28NAM0Z1pYBAO8ASC/8CgBo1wBw971R6QI+6arWoqkHESdTMRFzP/pTiIU2yA+eJFv9zoO/SkRDbsV6cDpKxuVvXHwwGXeys3meOeqypBcJCkefKCik1q0Fha3lKt+V2n5/Cr3N80dOPICdpqWgZQZsxIwKXYjrWt8bFbKDmUwJzy7zUnWZzIu4BM7dJZAthNyYS5APfHkAkDsAFBlUg4LOAdD8JmXLAMB3AKhLxb41ADQnBs/zQod9rzcalF/DOOhm/jIWJpXf2N3OLYz3/c1LhH35j+NZ1HdtlzPH8VGXM+vYVdCHarJwngP6Huh5wLOBjNV6EHguYBZgA+AhQNOCycnjInbanToZXzGG8f5BfAyT6epFjUk9wCjwGKAEMDkLVZOq6RxAKWCoiY31KoYFp8en0AK0a+8FORnrA+apQs8GdNjKJr8OR0dXL/crNSA9EbXfEWCuXotUvlIFAT1c0glRBUZSWwxAv5d2HqUFojrkx/rmOCzyX3jU98c/J6m8OrlUjT7D0Nb1UijeTz8theLQZEIb7zNhQXtnUqGs7v6SkoVHuz/2YO8/ \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.png" new file mode 100644 index 00000000000..3ad5782c8bd Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\206.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.drawio" new file mode 100644 index 00000000000..31e3097a914 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.drawio" @@ -0,0 +1 @@ +5ZhNc5swEIZ/jY/uYLDBHGPsuIdmpmMfmhxlkEGtYKkQBvLrK1niq46bJm0Zpj6hfSWtpH12NSMmlheXW4bS6AECTCemEZQTaz0xzdnMsMVHKpVSXNdRQshIoAe1wp48Yy0aWs1JgLPeQA5AOUn7og9Jgn3e0xBjUPSHHYH2V01RiC+EvY/opfqFBDxS6tJ0Wv0jJmFUrzyzXdUTo3qwPkkWoQCKjmRtJpbHALhqxaWHqQxeHRc17/5Kb7MxhhP+OxN22UPg7CLH2z06MV1Pt0bxPNVeTojm+sAT06bC3+ogt8wrHQf7ey73uTpCwqfZmdKdGGAu0rLtFK1Qfu9qH2Izh1rUQWg8mmJ3AqIwVkVEON6nyJc9hcgjoUU8psKaiSbKUkX2SEocyE0QSj2gwM6OrOMR274v9Iwz+IY7PYHjHgyjWfyEGcfl1fDNGigimzHEmLNKDNET7DojdSLPa7vopIWWok5G1BrSiRg2nltWoqFxvQGdeRVdlqLk/fQ6CaAE72WcjaaW+z8oO+7IKFtDUV7dEOW5NTLK86Eor2+IsjUfGeXFUJS3N0R56bjjomwPRXlzQ5QX5shq2bmMaCAeDtpMIJGhZZAngQzaWoYBGI8ghATRTwCpDu9XzHmlnz0o59APvogOqx7l/A+L2nzq9q1L7VxZlbbey0ie4deExJEhZz5+/U3BEQsxf23cJXGGKeLk1N/HX+e3HKpK72+oSh1jZFXqDlOlJeGdIhXWU12Vot2WqDT+fYX+QUXpqZ+BCI/ty/enu7e5i2sX6kbQs7p/IN7qSF0ZF47O6JvzvJQNwmx/pKjh7e8oa/MD \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.png" new file mode 100644 index 00000000000..fe6956b9d09 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\344\270\255\345\272\217\351\201\215\345\216\2062.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.drawio" new file mode 100644 index 00000000000..b92e5c29cc9 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.drawio" @@ -0,0 +1 @@ +7Vtbk6I4FP41eZwuIDfyKIq9D7tVU9u1tTOPKGllGo2L2Or8+k0kIEFQ2xvOtOWDyUnI5ZwvJ985IoDdyeo5CWbjv0TIY+BY4QrAHnAc27aI/FKSdSZhjGaCURKFutNW8BL95FpoaekiCvnc6JgKEafRzBQOxXTKh6khC5JELM1uryI2Z50FI74jeBkG8a703yhMx5nUdehW/gePRuN8ZpuwrGUS5J31TubjIBTLkgj6AHYTIdKsNFl1eayUl+sle67f0FosLOHT9JgHEuvvH2+rZPbTI6O56/T+WTrPX/Qo70G80BsGDonleN5ALTldaz2Q/xZqnd6rmKZf5hsrdWQHB89W20ZZGqnvTj6GXMwgF2olFCM6cnXSiLLiLcdRyl9mwVC1LCWOpGycTmJZs2UxmM8yy75GKx6qRURx3BWxSDYDwddXToZDKZ+niXjjpZaQsoFlFZO/8yTlq0b12YVRJJq5mPA0Wcsu+gGCtB01kCHW9WUJFlo0LiEilwUaiKNi5K2tZEGb6wOmc2pMV1XxNOyoMyBrUzHlplrlvpP1t3Lle7nSU/u2itpa17I5eLhzaip6lOsQi2TI92wA6uMcJCOeHsLorl1Kesc1es9lCY+DNHo3l1tnDD3DVxHJjWzNblPD7IRU7JltUz9VPn6VgaiFnxwXOdR2EEUkX4keFkHXbIXmLJmSdmbZAKfQyelYgo1uYD4Lpqd7gpIzyQTdetdQyLLpfg+PQZnpMVDbHgPdysreJ7Iytu/MyvhWVu59IisjaFoZw5atTG5l5edPZGWX3pmV6bkcbxWl30rl7zmnk+UtwVOVnN8VrLCo3JgVol+BFeIKUAg9lRVC10Qcdm5K/NxzAVYbRFgH4JLD0irB0t4LyxYAhtsEGKneN/hEgBHi7gs7cKthBzsTfRdEBTkSFbBNVFBoPxEIMUQulhe4bRoTI/uJ2YwhV7YgYtETXRIiexCDkVXXeiPA5Fz7+rTH/0S0B7M7oz12XVryXN6z/4Kp5z2HLrI2eI9Vb8sbER/XuDBsZl4n1H6iNqaSSGOJIrcy/NF3ll07TD4Jql3CrTyQ0+iBlKO5oAeyD3igbLoGDySdQWoeCdOD6CNTdjdaFMTRaCqrQ4lgLuWeci3RMIg7umEShWHc5NsSsZiGypP1LpV2typpd7Lrm1AN3p2r+abmXOmFAeA8AKCMS0wAoNYB0JxGvTAA8AMAGz56bwBozrBeGADwAQDl8qv0FLUNALLLRiUFfNFVrUVTDyJJx2IkpkH8pxAzbZAfPE3X+p2HYJGKhtyK9YRbSsblb1ycmIw7mmyeZ466LOlVgsL+JwoKqX1vQeHFcpUfSm1/PIV+yfNHjzyAraalmBmvUTMoxBTWtX40KHT3JjIlOttMS9UlMq/CCMiDEcgW4t4ZI8gHvj4A6AMAyhmgewNA84uUFwYAegAAlH4PuxsANOcFzyOhHa/nd/vltzD2sszfxsKk+hN76xaGu3TzGlFf/tt4FvTdmnHmOD7IOLOObcV8Tk0SzsfA80HHBz4CMlTr2MAngFmAdYHvAHdTMH3ysAidtqdOhleMQbh7EJ+jdLwYqDFdHzAX+Ay4FDA5i6smVdNh4LqAOU3eWK+iV/j05Bi3YKPae0FOxjzAfFXoIOD2LrLJr73+wdXL/UoNSCai9tsHjOi1SOUrVVDQgSWdUFVgdGOLLvA6m879TYGqDjmVuzsfFgcDHnvB8G20kVcnl6rRZ9hGul6KxL3N50KReOVdkCIfVvKEhds70xXK6vYfKVl4tP1fD/T/Bw== \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.png" new file mode 100644 index 00000000000..5c80cedf7d6 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\205\210\345\272\217\351\201\215\345\216\206.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.drawio" new file mode 100644 index 00000000000..c324e801447 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.drawio" @@ -0,0 +1 @@ +7VvLluI2EP0aLacPlqzXEoPpLJKcySHJPHZurAZnDCLGdEN/fSQsGwtsoHmZTHNYIJVkPaqurqoKA1BnvHhMgunoNxmKGMBWuACoCyB0nBZRX1qyzCSc00wwTKLQdFoL+tGbMMKWkc6jUMysjqmUcRpNbeFATiZikFqyIEnkq93tWcb2rNNgKLYE/UEQb0u/RGE6yqQM0rX8FxENR/nMDuFZyzjIO5udzEZBKF9LIuQD1EmkTLPSeNERsVZerpfsuV5Na7GwREzSQx6I2vhvtYG/fv/jz7cvuP+9P38bfTKjvATx3GwYQBKr8bwnveR0afRA/p3rdXrPcpJ+mq2s1FYdIJ4u1o2qNNTf7XwMtZinXGiUUIwI1eqUEVXFex1FqehPg4FueVU4UrJROo5VzVHFYDbNLPscLUSoFxHFcUfGMlkNhJ6fBRkMlHyWJvKHKLWElD+1WsXkLyJJxaJWfU5hFIVmIcciTZaqi3mA5HY0QIbU1F9LsDCiUQkRuSwwQBwWI69tpQrGXO8wHaww3aaKJ2FbnwFVm8iJsNWq9p0sv5Yr38qVrt53q6gtTS2bQ4Rbp2ZDj2odcp4MxI4NIHOcg2Qo0n0Y3bZLSe+4Qu+5LBFxkEYv9nKrjGFm+CwjtZHC7DjnK2N2vGnPbJvmqfLx2xiIMPwAmQupA13qknwlZliEmd2K7FkyJW3NsgJOoZPjsYRqaWA2DSbHM0GJTDJBp5oaClk23c/BGJTajIGaZgz3Wlb2PpCVXX5jVsbXsnL3I1nZsa3s4oatTK5l5ccPZGWGb8zK9FQfbxGlX0vlb7lPp8prB09Xcv+u8AqLypW9Qvd/4RVuAAXzI71C6jAbcRRe1fFjpwKsMoho7YFLDstWCZbOTlg2ADDcJMDI5n1Djw07XLYr7KCNhh38RPSdERXkQFSgJlFBHeeBIISRy7C6wB3bmC5xHrjDuctUi0ta9EhKgmQHYtS4TSImPxaX93v8D+T3YHpjfo9TlZc81fHZfcNUOz77brImHJ9WtS2v5PkQ68ZwuH2fcOeBOphCRjHEiG0MfygF4eph8kmql3AtBoK1DKSJ5owMRPcwUDZdDQMpMkjtI2EziDkyZboxoiCOhhNVHSgECyX3NLVEgyBum4ZxFIZxHbclcj4JNZN1z8VNzOamgnRKeHcr8A4vxk31ydIzAwDdAaCN69oAQI0DoD6PemYAkDsAVg7prQGgPsV6ZgA4dwCAUkp9HYM0DACy7Y0qF7BvqkaLth5kko7kUE6C+Fcpp8Yg/4g0XZqXHoJ5KmuSK60H3FA2Ln/l4shs3MHO5mnmqEqTXiQo7H2goJDwWwsKz5asfFdu+/059HOeP3rgAWw0L8XseI3aQaHLkdXKjkxMsZ2pTAXPRhNTVbnMi/gE7t0n0OxEbswnyAe+PADwHQCaDeCtAaD+XcozAwDeAaDvLnRrAKjPDJ7mhnZ9r/fYKb9Vu9PP/GksTDZ/ZW/cwmjb4bxE3Jf/PJ6Ffdf2OXMc7/U5s45NRX2wIg3nY+D5oO0D3wUqWGs7wCdAxTG8A3wI2Kpgc/KgCJ7Wp04FWJwjtH0QH6N0NH/SYzIfcAZ8DpQzy9UsTE+qp8OAMcBhHRubVXQLTk8OoQXHrbwX1GTcA9zXhbYLWPcsm/zc7e1dvdqv0oBHV/vtAU7MWpTytSooaKOSTqgucLqyRQd47VXn3qpAdYc8Yrw5DouDJxF7weDHcCXfnFypxpxhxzX1UizurT6XicUR3GbCgvZOpEJVXf8pJQuP1n/tQf5/ \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.png" new file mode 100644 index 00000000000..87bf512ec50 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\220\216\345\272\217\351\201\215\345\216\206.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.drawio" new file mode 100644 index 00000000000..69326285015 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.drawio" @@ -0,0 +1 @@ +7V1bc6O4Ev41PI4LXRGPtuPMPpytmjqzVbv7dAobbLNLjBeTSbK//ggMGLC4GMuIDCSpChJYQupPn1rdLVlDy5f3r4F13P/q246nQd1+19CTBiEAOuX/opyPc45pGueMXeDayUOXjO/uv06SqSe5r67tnAoPhr7vhe6xmLnxDwdnExbyrCDw34qPbX2vWOvR2jlXGd83lned+7trh/tzLoPGJf8Xx93t05oBNc93Xqz04aQlp71l+2+5LLTS0DLw/fB89fK+dLyo89J+OX/uueJu9mKBcwjbfMB13M3//vXf0Jevf32cdPzP0/r0JSnlh+W9Jg3WIPV4eYs1v9hFF/y/9XLkF4f16XhOxw9sfV4vb1b4kfQV/efVT298OcWSnPMHIDm+X26mpeppMfyFzyVltaXZuTeAhYogbxiXP08s3vZu6Hw/WpvozhuHIM/bhy8eTwF+aZ2OZ1Bs3XfHjt7N9byl7/lBXBDabh262fD8Uxj4fzu5O7ZhrnU9q/yHE4TOe2XPg0yefCA4/osTBh/8keQDiCYQSMYATNNvOUQlWfscmNI8K8HwLiv5ImZ+kUj6BqlDgdTLXXyw59Hw4amDf3CK3crbHXz8kU/8mU88Re3Ws9RHkura9459NURLPc/f3H8NNk5Nk1HCHVawc8KmAXEtyZykiEBSaV7geFbo/ii+rkh8SQ3ffDceRClQcBEohJQQcG5m8qn8WC8XZJYK0ksFnfvhqqAYTVmzuwMMtaIVCTQCR0QjmNGCUBFRTCO4Usqno3XoLtQrrACxOLO8c3U/h5Qh0oclZdLXWEYjGsuAFKWMkWIp076kTEYkZQzwsKRs3Kv4vbvhH7nrP1NFj19ftL4okSp9maqYJQavKuJPqSrSjqoi0VkRoxj2qiqyeyEpXIvoDQBLgazngAxqgfwpIEmUQrI8p3VevTA2gwxDA0BsYAoBKmlE4ts9AdaUBdjPw4i0JfyQSvgRZM4oQgRhRrjyAUARjMCcmcA0MeN3MNWNbtDEuLgGI7jfhXVqFn38mguPaM2FwMC0cSCyy96rqNXPb2JaappHP8WsCHSx9HuaFvXCdAXM4mRmgJkBiAGZQTiFsVLxradMJCwmrQQLX6EvzoKVnCV5bUlHtLYkdGicJTL63q/Jy1eMMt1/BgySX8jO9CxdwZE88c0JXN5hTpDkSeSzdKAMW8/CJpuZlEIT6JirWowUYUj1GTFw5CElkBFKaDdCI6ioaOG+Fa1q47Zk0jJGRFoAVFgtlJFWtXVbsj7NRqRPZ/IajJhF5u1ui/YZ+bx2pmZNGaucWQDja3QzU2INVrSeE8BnaD1n9+k4tQBKZ4RWmpcIQjOMLm/Rs7J8t4n+J0CqOqN6LwAsmxuubPePxpjI5p5NV51DMSq89J97KjNK7nig2h0PpNmfb1pmieih3vPXflpqXBilIZTN8xdTSR8UlpbkXR0g1CgV1HP4VtqOHiJ7zBGpxaykFkPVJhsoMjM/RM6lpfIm6+DLJ7kQEP+5LgzctkL+KaFDBgedaqOubIrQRyRnXJIzgqrlXB3KOwV5dhazaQ5NzCJz501aZU+RYTK1ytZWkWFplV0jvQxWjPRCPUd6QZGt9f6Vi3zDhgqMnftCGcbK007Z29IaY6AUm1XUWrDSyC0ozQp809JZJpzabmPK7TBUACeGUU0oFtKlhGIZVSH0faFJZKl9jGZERqQaXVGRctXo7gjlnmKxlExbhtJpC9WFVkEqJbSKiovJFuIqQ6ugyBj8EA6CI6IgUp5ZsGIKSme2UVEQbEtB5nApCIFadpBDQQjX8tyjt3r3ZkNGI6IgY2gGIgQlUVD7ZXi35b5MCmrrdkRKoxaM+gBzORRkNASYK6Wg3mzUY1qIZU7nwWhBsmzUn4mC2mpBSO3eO1JLQaYUCiJmHQVhtRTUWxyxMSIKonhoFHS3Bbk7Bd0YzymThIy2JKR0/3k/JCTer5f55pSSULVFuvueFV3ISiuqsWfNhNqKaIxoc5JcMKStDG2BtXk+auPmbS2cMsLiwCnyTDKw8qSUZFmeuzvw5IajPNqjtYgIyN1Y3jy58eLatlfFgIH/erAjvnuSxWAl5y0RxO3UDQn5DHabNXvjWaeTu6kgsRks7J+r5TGJfISUGp2NkoMCl2eb1lad8klfsFTQo+nitkjiBigAPkvk5rSZDnH9vPbYvZRqMcKazl9sjZESf9CenZypwlWHkdPeOkaXobX2SuqOiGNP/JXD5KDfSOgbPjlY7iEGAYjTnmcdT25c2PmJvevZ/7E+/NcwrSZNlfVS23LYVqiX0g1z1ltJS+PSBg4q0EsN2KdiilscGJHKibczdC3vv1zBtw67NiK7Fokd+MffUnUwyjhGCHSC1Q/ehSfteoNQMj2H/jG56Tnb9LNrPwz9lyQRJL2VFRp3FVnwP955y0gLJrw1S54GlzT/ix4PwqV/4KK33FiQjnUK35xT2FbqNUOglkmEsi6H0MiTtcgUK0WrKyuLLleG3hvWm0XdTiLk4vO8rQvk7kaTz6W/9eK5bM+VQOdwHyyabb60V1RUG0Elo6IpHntChHgpcD1H9AuQ6hMNJAOkKZJ7Aoj4cASBj69fhFQbMSUjBE4I6YQQphoh1ccES0ZIk7N/Qoj45A3BSqRfhFTbICUjpOmAwgkh4rMOddUIqT59QDJCmrz1E0LECFGuqVZHtUpGCJ0Q0gUhWLWmmpppH4+QJnf7hJD6fczKEFIdlirZF8oq0dDC0TlehBDVmiqpNqNKRog5IaSbwUw1Qh7xNWZChBTOuJgg0h4iqlVV0iImdfK7SfW7fRRFqswNR3qzlp5rmPTQ6gE4HDccaREhOkJxDMQHRiTs2W/p4bjrOKPxIkS5E4zIN0/edtzdtTt1skF0g5JyG0RvdszGYzQniAzTXZZW1gNEprCdThBR7i+j1ZZM2RCZHGbdIKJaraXVpswpLGMICFHuMKO9RYdOwX/dEKJaWaWP+MIrIWTu+gb48SJEucOMVhs8JSMETgjpZl1TjZDq8FDJCLnrS/NGjBDlmqrAALsi2mKlzVfaCmuLZ20Oot3lpq6ZS20FNRZftPr6CNMU2dO+uuH+dR3vWF9pJtNWpsYMzeS1sKjSqDqiMRZtZq/AVPIWTxmWgzbABVi8c55o5kIzV9HFHGvsSUojvz09N749by/vAT50ovY+ayZN3oV3ftQVhjZHuT4xogvTiGWx1Bbz+OHn+MKIHkj1lcHt2vesteMtrM3fuzi/XDnvmmTzKcBJOrdLdBH/th2CDWe4lb/my7gefFDksizvA28x+ngy8CNEZPe+8r7Z/+rbTvTE/wE= \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.png" new file mode 100644 index 00000000000..564685ab7b7 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.drawio" new file mode 100644 index 00000000000..11133b532ad --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.drawio" @@ -0,0 +1 @@ +7VnJdpswFP0aL+OjiWmZOEm7SIdzsmjSHQbZcAqIynKM+/UVRmIysR2SYKfuCr0r8TTcq6eHGOFJnH3ibhp8YT6NRgj42QhfjxCCEJjykSPrAnEcqwDmPPRVowq4D/9QBQKFLkOfLhoNBWORCNMm6LEkoZ5oYC7nbNVsNmNRs9fUndMt4N5zo230R+iLoEBtZFX4ZxrOA90zNJ2iJnZ1YzWTReD6bFWD8M0ITzhjoijF2YRG+eLpdSneu32mthwYp4k45IU7eumBOPrJb52v09m3AKzusgvl5cmNlmrCI2RG0t/VVBbmeUE+3TiVhWS6SAt702DGZL9yWmKt1sr8vWS64mKxYfJSNkBGmlWV2ivQbuSAC09lbxqujQA1OkJyYpJ/aVytglDQ+9T18pqVlKDEAhFH0oKy6C7SQhSzMKN+PrYwiiYsYnzjCM9m1PQ8iS8EZ79orca3nCkAZedPlAuaPbvysORTbgTKYir4WjZRL2BTSUDtAaTtVU1RCgpqYtKYqzQ8Lz1XNMuCYvoFrKMO1ttLnPiX+faRVsIS2lxWOW++fqgbj3XjOp83KK21svquPfW3tmhr5eXI2ZJ7dMeUsYodLp9TsW9DbDNZY8roYEpjnEauCJ+aw+2iT/XwnYWbTaSFQppCMYyWAoppqrfqe73tyGk5Ai1HxTpsOdqoqZx2f4Hhg8LKG4QRdEZhhLTUUarlWGGEPMvyInWT/qRuaQV201liRXf/BsslXafCsjHUXsZntJcRaLJM0JFZNodi2Tgjlg10Yixbr038slA81MqPOtGT5Srryw2d9JWpYmmcfKpIPmSqaPZMFQ1gNzVK0KCpov1aSXZ+i4A9AtNCBjUhw51C/hCSNI4pSdI+00hPSRJsj5FNkAURsYiJIG5lRN3VAwnWeRfBnnRENA+UHz6q/Bw8NjE2pDzkuYvKm0etRuCMHeg4xEYQEhNYPbXZErmBh/2y1vei7//RRc7oo6t9Q3f0RA12Xcy+NlPbfcB1Z2r7DtIPcSxC0M3+QKma0TivoNM8zUw4tqBhIdsyZDSxW+4PvvGzO93oTnDnEIaKWWiomGWeUcxqXxSVd7xHi1ldt77/Y1b3ddr+mIVON2YRuDOcvE3MImRnYOwds6RZ/XQtmle/rvHNXw== \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.png" new file mode 100644 index 00000000000..673f3e32beb Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.drawio" new file mode 100644 index 00000000000..6eeaa610144 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.drawio" @@ -0,0 +1 @@ +5VhNc9owEP01HMnY+rCtY0LSdibNTKc5NOnN2AJ7KixXiGD667vGsi0bSGlKcTI5sfskrbT73grBCE8WxUcV5smdjLkYIScuRvh6hJDrOh58lMimQhjzK2Cu0thMaoH79Bc3oGPQVRrzZWeillLoNO+CkcwyHukOFiol191pMym6u+bhnO8A91EodtFvaayTCg2Q3+KfeDpP6p1dj1Uji7CebDJZJmEs1xaEb0Z4oqTUlbUoJlyUxavrUq37cGC0OZjimT5mwfT2s7i+U1/V7ePDOLv8TokYj02Up1CsTMIj5AmIdzUFY14a8BkucjCy6TKv/O2EmYR9IS29MbXyfq5kPTBebpm8hAmI5kU7WEd16jBw4CpSs1sNWydAnY0QJAb8g3O1TlLN7/MwKkfWIEHAEr0Q4Llghsu8EsUsLXhcni0VYiKFVNtAeDbjXhQBvtRK/uDWSOyzqeM0mz9xpXlxsPJuwyc0ApcLrtUGppgFxDcSMD2AqPHXlqIMlFhiqrHQaHjeRG5pBsMw/Resoz2s90ucxZdl+4CXyYx3ywp5q82D7TzaznWZt9N4G+O9tPY83mnRXuXh5HKlIv5MytjcHaGac/2nhthl0mKK7mGqxhQXoU6fusfdR5/Z4YtMt01UC4V2hUJxTwFVmmaV3eu9QNTpBiJ9KVV12Am0VVOT9ssFho+6Vk5wjbjv6BqhPXVgPPA1Qg6y/G+k7mgF7afzAPNvm2UP91rXHZhleq5exu+ol323xzIbmGXvXCyT98Qy632f+wOz7A/y8ONFqh8s+9Gy2yWl8yqeiuTIpyJ+VU9FcqqnIjvvUzE4lSQdW5LOkZJ0LUm6b1+SdEhJUoYvPIwpJgGF2991u69Vn10wlzESwAjxHP+FciXBBQoIfIHCz2riod4mqDeKz6plNsj1OqgyvbegTM95VpnBaZRJn1Um/k/KBLf9D7Ga3v4Ti29+Aw== \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.png" new file mode 100644 index 00000000000..af12915808e Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\226\234\346\240\221.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.drawio" new file mode 100644 index 00000000000..8379028d2a9 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.drawio" @@ -0,0 +1 @@ +5VnLcpswFP0aL+PRG1gmTpq2M5nJTBZNuiNGNkwxogLHOF9fYSSMME5c2/WjXqF7JK4e5+jqCnp4MCnupZ+GDyLgcQ+BoOjh2x5CEAKmHiUyrxDPcypgLKNAN1oCT9E71yDQ6DQKeGY1zIWI8yi1waFIEj7MLcyXUszsZiMR272m/pivAE9DP15Ff0RBHlaoi5wl/pVH49D0DJlX1Ux801jPJAv9QMwaEL7r4YEUIq9Kk2LA43LxzLpU731ZU1sPTPIk3+SFZ4cW19/kzzALJmPy8P39HlxdaS9vfjzVE+4hFit/N6+qMC4L6ulPUlVIXrO0shcNRkL1q6aVz/Vasd9TYSqusgWT16oBommxrDRegXGjBlx5qnszcGMEyOoIqYkp/pVxMwujnD+l/rCsmSkJKizMJ7GyoCr6WVqJYhQVPCjHFsXxQMRCLhzh0Yiz4VDhWS7FL96oCRzvFYC68zcuc16sXXlY86k2AhcTnsu5aqJfwExLQO8BZOxZQ1EaChtiMpivNTyuPS9pVgXN9F+wjjpYby9xElyX20dZiUi4vaxq3nL+3DRemsZtOW9QW3Ntbbv2PFjZoq2VVyMXUznkH0wZ69jhyzHPP9sQq0w2mKIdTBlM8tjPozd7uF306R4eRbTYREYoxBYKpS0FVNPUbzX3etuR13IEWo6qdVhxtFBTPe3tBYY3Cit7CCPogsIIcZlFKqZHDiNkLctZ6ifbk7qiFdhNZ41V3f0fLCMMTotleqi9jC9oL0Nqs0zwkVlmh2KZXhDLBJLTYtnZNfErovy5UX4xiZ4qL7O+0jBJX50q1sbJp4rkLFNFtmWqSIFra5Sgg6aK7q6S7LyLgE8EZoQMGkKGHwr5LCRJjyrJ9pm29e3FdfvIJciBiDiEIYhbGVF39YEE6+1LsOcTEdmG8sPHlB/FXp9hTDFxqUo+ILTFCL2+Bz2PuKqGMOBsJ01C7DsYJYe9WJvPov/+zkUu6M6F4Yll47Dru+yuidrH51t3WPrsHD2LUxGCbvYPdCwC67iCnn2YObDvQOog16EqhLkt9xsfmbjTjemEdA7hUDFr/XeiPd8t2QXdLSk7tZjV9Qlh90x+/4lRnfv3oUObF9k+qO01MVIZj1xGasG41Nge4xncNKAdNc8intv3GEMeBESlWi61ZchAnzqk/ENKkUsZZdsFNIrtRIvsLdFS5vKna9V8+esa3/0B \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.png" new file mode 100644 index 00000000000..c0f30c04c56 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\346\273\241\344\272\214\345\217\211\346\240\221.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.drawio" new file mode 100644 index 00000000000..37585459182 --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.drawio" @@ -0,0 +1 @@ +7Zxbb9owFMc/DY+bcg95LPQyaUya2kntHl1iiFcTZ8bc+ul3HBxIloXCgMQPFkjEx5fE/h38z3EMPXc4Wz9wlCXfWIxpz7Hidc+97TmObVsBfEjLZmuJonBrmHISq0J7wxN5x8poKeuCxHheKSgYo4JkVeOYpSkei4oNcc5W1WITRqtnzdAU1wxPY0Tr1mcSi2Rr7Tvh3v4Fk2lSnNkOom3ODBWFVU/mCYrZqmRy73rukDMmtkez9RBTOXjFuGzr3Tfk7i6M41QcU2HgfX9xfi98h44e3r/es+dF9PbJUdcmNkWHcQz9V8mUpfAx4GyRxlg2Y0GKcZGwKUsRHTGWgdEG4y8sxEbRQwvBwJSIGVW5cIV88yLrf/aL5E/VXJ64XVdSG5WaC87e8JBRxvOLcy0rCPw+5ExYKkp2fyhfYN92R/ahcZSUac4WfIwPDU1fuRviUywOFfR2NOFrgNkMQx+gIscUCbKsXglS/jjdldsjgwNF7QSCriF4NkE76JKgZwge/modQbDfJUDfADwboOt0STAwBA/PjcdMop0SDA3BswmWbks7IKjEeonoQp3pFglUwwq30Zk8FOhVmsp0VgkR+ClD+WisIB7JRx5xoXh6EgUECgKRFHNVacwoRdmc5K3lsMYJofEIbdhCFOcpUjmuwqlkbUTJNIXjMVCRTQ6WmAsC4cONypiROM6vc0IoLYF27KF145/hGvJEeH3YOeooVQWvCK9UVOb1VXq1j3HsInBJSvFNaF2JftQIGnolCKKPEN2hdHoM8zrSmLPsR+H+0pAxIoHdLWHA5spWJqQmDJHPCzKT4klR95UJwWYqwdXY7BrNB8YfwBuGaijnCR96M4S0vU/DWxbnwDcFBwB/lG1gNBcrPBfX84pT7p0K1zjSM9xreUbhqqWJYSRhXNJf8rUDtPeXs12BAYgJzcP9BGYAnLbMNPo30xLEoFWGdo3hY35aA/F0iOsqwM6YOkawWxPsMNRNsO36wpNR7JYmhqZQSxvJ9oxknw7V1UyzfaPZl6Ooi2gHRrRbE+3+X6Ltel7Xol1fJjOi3dbM0PCQSRvRri/AGdH+EGqomWhHRrQvR1ET0XbqK2BGtK8l2pF2kfbuQY8R7dZFu+mRmC6i7dQX4Yxofwi1YQbobH53jWhfjqIuol1fAzOifS3R9m3dIm2nvinMiHZbM0PDhlxtRLu+CGdE+0OovmaiHRrRvhxFXUTbbEJrT7SDqCrau1/KdCfaZhdad6Kt+Ta0omEj2qdA1Wwfmmv2oV2Qoiai7dbXwIxoX020tds57pqNaJ2JdtOvtrQRbbMR7T+garYRzTUb0S5I8eqiDcn9nxrkeaW/hnDv/gA= \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.png" new file mode 100644 index 00000000000..c0ce15b72e4 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\223\276\345\274\217\345\255\230\345\202\250\344\272\214\345\217\211\346\240\221.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.drawio" new file mode 100644 index 00000000000..1ddbb02dc6e --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.drawio" @@ -0,0 +1 @@ +7V1dc5s4FP01PDYDSAh49FfSh3amu+nMto8yyDYtRi6WE3t//UogMGCwiSHgjZlkJuiCJXHP0dXVMSgKmKz3TyHerL5Sl/iKrrp7BUwVXdc0FfE/wnKILbZtxoZl6LnyoqPh2fuXSKMqrTvPJdvchYxSn3mbvNGhQUAclrPhMKSv+csW1M+3usFLcmJ4drB/av3Hc9kqtlq6ebR/Jt5ylbSsITs+s8bJxfJOtivs0teMCcwUMAkpZfHRej8hvnBe4pf4c48VZ9OOhSRgdT7w9OMPePm6oQxPv88OwRr9+uvwSY9recH+Tt6woiOf1zeeiy6zg/QD+rMT/RwvaMA+bSOURvwC3djsjyf50VL8HSV18M7ME6N0QlqjznvHQeSF8evKY+R5gx1x5pXziNtWbO3zksYP8XYTI7vw9sQVnfB8f0J9GkYVgcWCIMfh9i0L6W+SOeOa9lxV08ZfSMjIvtJ9WgoKZzOha8LCA79EfsBMcJREBglDXzO0kKZVhhGJDUsiLtOaj1jxAwnXG6ADJdAVXRy4IzEGeCmgAcm7ld93ePiRLfzMFqbivtW0dJCluA3inoyagh95P+gudMiZG4ByOONwSdgljp7ikvG7UeL3xBYSHzPvJd/dMjBkC9+ox28khR0l8UrCjvQCnvFtyk9lh1+hItMyHnQL6qamQxOipCcJm2wrfxbkW4mddNJKRJzUJ9dzCVaGge0GB9dHgkwwiQ2T8tCQ2uLmPkbEsMx8xIB9RwyjK5THd4SyYd8YyqgrlKf3hLJWQNnuGWWzK5Sf7ghl27gxlK2mOd7eYz8yxz+TnI4fHxM8UUjyuzQrTAsdZ4XG/yIrLBAFgSuzQkuzchUZmt5p4mc3JVjpIkK9QJeElmqGltpZWvZAMNQnwczCfJPOP29edkDrzLIDar0uO5Kbupp+LdLCrEkL2CctLE17QAAYAFoGn8G1PJqGqj3Ymm1Di5+BSDWvjEk6OkMZQ1V7pYzWVeIzu6PEBxWXqn0nPsk02Grmc36KKc98Lk1lPUxMWgWWHaU+KDdlaHaeOEB7MDXD1C3T0A1gFaqvG4KQXVpN0oha2oWuIlCZ8PouEejzHUUgCAoJj9l3BCrTRK+JQPWT3OuS6RYjEKobgcANRyDrbHBoJwIZ2tkw994RqFrIFYGmxQikXYhAcXMVEYgHA5YfEvkIIodMNtxIE/a9ZcCLDmcw4faxCC2eg/2RPLH2XNevim0h3QWuiGTTtrIjKx+bgH4am2AJ34tfFbUXm6o13pYJoA8EEMMdFtLj3glQLf+2TAAwEEBkGfqtEaBMGX4XAsCBAALcwgLZUPsmQJly+y4EMAYCKBmF/1YIkFT8/gSwBgIop98AGyUSWbcEKFNCC97frvBGHDI89wvL0zKnbfnChclnMIXbHI4s9gLh/egzDvV9vNl6UWXxFSvPd7/gA92xpJmkVNQRXEysRamOgByLzBdtBWqUgwkh4wQmpHYpJCSw1MGJ3yfzsP83cRgOlnUgO4XEDenme7KCF4aNWIeScPbCXbiVtpLxxuhGnvTJIvnsnDJG17IQSm+llUauMsb8lztvoj4YisHvZsLL2rHMf8XlIZvQgEOPvQhIgrfslWxZXdTPDIGzOkQp1vDdsK7WBmsHZbVWUPZ4dNs3CcxNKBc9ao2PlGvMJsrRX/iRkLbiUZ0EzWih16BFl6yofryyZVaoAyPexIh9ng29EaQFQa8eQRoJevdLEK1kvd8tQ1pQ/OoxpJHid8cMsfpmSAuSYD2GNJIE75chKfS9MaQFzbAeQxpphvfLEFCiKXXLkBZExXoMaSQq3jFD+s5UQQuqYz2GoIEh1zCk7JupbhlS/YBmywwxB4ZcxZC+M1VQJoi+C0MafXNxvwwx+s5UQY132wfJvFXJ/JCHtDcFHXSmlcYtDAGiegDejoIOygTSAY4bka9BZ+Jkxc4yw3g9T5De5WvQmTg5HhhyFUN6XxR0Jk5e2oJmYMhtytegM3FyOjDkGob0Ll/DzsTJS+8GDwy5TfkadiZOPg4MuYYhvcvXsDNx8tLGSgNDblO+htXPeJ7A2IwynyvZUGN71ftlSO/yNYQnaIjXs59lUXow/4YCDdmKLmmA/S80cq1A5xdh7CCfx8c7RvPYHTeseDCUfjbrksHy2s26ar8I3gyOzva9vDTpf6QNG8zivpd9bxkDy1TIoo9r7WWmvmU0vX2LvRbHH6y7Z0yvu1bZ+b0UzPyGDQYAZWffumGDfX6jM7vXXatgZ+9rN3ps5sO8rGeifHDq/W1N2Nn72o2eivkwBLD1GyOAUaJNzQxlPFNGM2UGFZ47jDRlhhQ+jdoTZaYrVnSQJ4mTzuVHHvD53rZFSlykxpPHVru5qNOaKbalzGzFMhWbt2KJRkVzhmJZiq1X0UP2ItVD52EdomqwlKi8MXus2DNxMIKKNW3lJr9NHy/2nt8v9wAfGeJ+HxUbyb5w5wtXmMoIZHxiigPbjLCYKONRdPFjdGCKC5IE5uaGjY/nxB9j5/cyshcb566RSwkNynImNRxHP+0MPlTYLgWVDL50pDUcfbx4/Bcq8XR9/Ec0YPYf \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.png" new file mode 100644 index 00000000000..33f3c6e3ff3 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\250.png" differ diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.drawio" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.drawio" new file mode 100644 index 00000000000..8048a1a724c --- /dev/null +++ "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.drawio" @@ -0,0 +1 @@ +7V1dc5s4FP01PDaDAPHxaDtOOp12p7PZ2W32ZUc2ss2WWC6WE3t//UogMGBhE4OBFqaZKbrCCO45ulwdybKiT172jwHarL4QF/uKprp7Rb9XNA0A1WT/ccshsjiOFRmWgeeKk46GJ+8/LIyqsO48F28zJ1JCfOptssY5Wa/xnGZsKAjIW/a0BfGzrW7QEp8YnubIP7X+5bl0FVltzTraP2JvuYpbBqYT1byg+GTxJNsVcslbyqRPFX0SEEKjo5f9BPvcebFfos89FNQmNxbgNS3zgd/G33/s9s92oH3Bn/w9/DH+MvsgrvKK/J14YEUzfXa98YzfMj0IP5g/dvw+xwuyph+2IUojdoIGN/tjJTta8v9H8TXYzcxio3BCckWN3R0DkRXGbyuP4qcNmvOaN8YjZlvRF5+VADtE202E7MLbY5ffhOf7E+KTILyQvlhgcz5n9i0NyHecqnEtZ6aqSeNpX8UPjgOK9ymT8N0jJi+YBgd2iqi1ocBREFmPcX1L0UKYVilGxDYkiLhMrnzEih0IuN4BnSaBLu/itTvifYCV1mSNs25lrggO39KF53Thnj+3mpQOolToyS3ZBXN85n510XtRsMT0MiWxm+mYp7ik/A4lfo9tAfYR9V6z3VkGhmjhK/HYkx1hj3u6gN3Uc3hGzy0+le5+uQs5AN5ptqFZQDMsw4zvRFzWUO1srZ5tJfLaSSshcRKfXM8lvTAMbDdofX0kSAWTyDCRh4bEFjX3a0QMoKrZkGG0HTKMpmAe9whmS+sYyrAplO97hLJpZFGGassom02h/NAjlIFqdwxmq2qWt/fot9Txc5zVseNjiscLcYaX5IVJ4bZ5ofFT5oVWjihJfHh3XmjYWcbFCDeU+tlVCSYdRqgX6BLTUk3REpyl5e0JBjtFMDv/wtGuJJht2ecGHlqrAw+nIvuuZ4VZkhV6p1jhGODO1HWoGzYjhAayYEIA7hzgsJDCagxTta4MSdA8wxgI1DYZEwuDt098HnuU+OQHMdBuOe8BMmWyauJz/g0jT3wuvcmuj0CwbOKjdioEWU7mjQGc7PuEBSgLQIu9dqAGdTt3+dIvLU16mbgRIL2FpiKQ1lQE+tijCHSilrUfgmSq6K8VguKofzkGaT9TDJLX1huDoLy2qRhULOXyUFNjDAIXYlDUXEEMYn2fZvtENoaIPpMOOMKEfG+5ZsU54zRm9jGPJN4c+SNR8eK5rl8U3QKyW7s8lp3vH++Y/QPZ4KTrp8HJkPA9P2irLzgVq7w1E0AbCMBqTCun8rdOgGIBuGYC6AMBFD4o7hoBZNLwTQhgDATgESCfn4K2CSCTbm9CAGcgACdAXvKXzA01SwDnxNk8P38SReHFrB9IQFdkSdbI/0zIRgDyL6b0INbeoR0lilzhV++g0siUULzQr64podKjjWoLshpTJqc90gWcvDLZ9oysVlmZvGqC9f0Tudf3v9KyQLcmRwDIjsmtrCwADV1W+15ZAKhnJ9QYP9ucHonJePukwBySAh5mnI5lhVrxWs6aCWANBAjDAewaA5rTBgdpIExRzI4NDDSZOJjz/naFNvyQopmfS1BkTtuy9xYVQwTutjlDFnlr7v3wM3Pi+2iz9cKLRWesPN/9jA5kR+Nm4lI+k3QRthfSTNKc23i2qAemRLERMFmSjuqojaaSMg2vACf26NRD/u8sJUfrZRnITiFxA7L5I87puGHD0xAcTF+ZV7fCJulvNBwp8kofL+LPzgil5EUUAuGt5KKhq+CY/THnTfjIEbKnmbAyOJbZHz89oBOyZtAjL8QWoy19w1sqRf083y9z4RLW+WV89WFdg1ynlorKHotu+yqBuQrlwm/hoSPlKrOJMFAXfjiUWrGojtfvoIV5DS3MRllRg4ZXjhXqwIh3MWKfZUNrBJGtkLwJQSpN9PaXIEDydZ9GGRJnNbdniDYw5CqGSBYyNcuQ4m9518yQSuPB/jIkgb41htSgGpZjSKW55P4yRJdICs0ypAZZsRxD4MCQqxjSdqaq1yA7lmNIpZmH/jJE9sX0ZhlSw5rFcgypNDXRY4a0nqnWsKixHEPsgSHXMAS2nqkWy6iz6+lRsMBtgD2/9rE12It10gqwgwJRdMA9xr31pLJY/qyCe4HWOeCe/zpwW7gbxaJmFdwLFMwB9/yuJq3hXnVDSjnuBbrkgHt+s5HWcJcJkDXCM6yEOIH+kIW0tYURRmPKYtTCMO479rbuLowwZGriAEdHViUYjSl5BXtJD/2146sSjMaUvPHAkKsY0nqq39iCyEubTg8M6eaqBKOxxZH3A0OuYUjrqxKMEvtH9hiettNEWKzilf5GdLkOXOkb0f1lSOtLAmCx3lczQyptY95jhrSdJsIbK4M/Nzytz8fDGn46plwHrrQdb38Z0vrUPTQGNLozoQ7hgEZ3prmhOaDRnclnWKxI1fwuq7Sxc38Z0uQ0NWtt/s/u9dMfSH3Wta+Pf/+528h+LnMKFTb6HE2VqaGwQcYIKFNTcVTFmShTTbHDg6xoNU+2NzjyRF8sHId/xyJPnUePrnYzfk17qji2MnUU21Ic1orNG+XNQcW2FUdTCuQqcReJxDULyhAZGFIis8acseJM+cHIUOz7Wh7y6/3Dxbtnz8s8MLbC531QHFPcC3M+d4WljPSUTyx+4FghFhNlPApPfggPLH5CHGo6tx2Jj2bYH6P592VozzfOXCM24QCGKKd2yxiH/5IeeNKxJBtoXI7G8aYmkswl2cGk4q4mrHj8HdxoJ6zjrwnr0/8B \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.png" "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.png" new file mode 100644 index 00000000000..70c6da26d22 Binary files /dev/null and "b/docs/cs-basics/data-structure/pictures/\346\240\221/\351\241\272\345\272\217\345\255\230\345\202\2502.png" differ diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\215\225\351\223\276\350\241\2502.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\215\225\351\223\276\350\241\2502.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\215\225\351\223\276\350\241\2502.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\215\225\351\223\276\350\241\2502.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\345\276\252\347\216\257\351\223\276\350\241\250.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\345\276\252\347\216\257\351\223\276\350\241\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\345\276\252\347\216\257\351\223\276\350\241\250.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\345\276\252\347\216\257\351\223\276\350\241\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\351\223\276\350\241\250.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\351\223\276\350\241\250.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\351\223\276\350\241\250.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\217\214\345\220\221\351\223\276\350\241\250.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\276\252\347\216\257\351\230\237\345\210\227-\345\240\206\346\273\241.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\276\252\347\216\257\351\230\237\345\210\227-\345\240\206\346\273\241.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\276\252\347\216\257\351\230\237\345\210\227-\345\240\206\346\273\241.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\345\276\252\347\216\257\351\230\237\345\210\227-\345\240\206\346\273\241.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\260\347\273\204.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\260\347\273\204.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\260\347\273\204.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\260\347\273\204.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.drawio" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.drawio" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.drawio" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.drawio" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\346\240\210\345\256\236\347\216\260\346\265\217\350\247\210\345\231\250\345\200\222\351\200\200\345\222\214\345\211\215\350\277\233.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\230\237\345\210\227.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\230\237\345\210\227.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\230\237\345\210\227.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\230\237\345\210\227.png" diff --git "a/docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\241\272\345\272\217\351\230\237\345\210\227\345\201\207\346\272\242\345\207\272.png" "b/docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\241\272\345\272\217\351\230\237\345\210\227\345\201\207\346\272\242\345\207\272.png" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\241\272\345\272\217\351\230\237\345\210\227\345\201\207\346\272\242\345\207\272.png" rename to "docs/cs-basics/data-structure/pictures/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204/\351\241\272\345\272\217\351\230\237\345\210\227\345\201\207\346\272\242\345\207\272.png" diff --git "a/docs/dataStructures-algorithms/data-structure/\345\233\276.md" "b/docs/cs-basics/data-structure/\345\233\276.md" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/\345\233\276.md" rename to "docs/cs-basics/data-structure/\345\233\276.md" diff --git "a/docs/dataStructures-algorithms/data-structure/\345\240\206.md" "b/docs/cs-basics/data-structure/\345\240\206.md" similarity index 99% rename from "docs/dataStructures-algorithms/data-structure/\345\240\206.md" rename to "docs/cs-basics/data-structure/\345\240\206.md" index 7e8f9811878..cd186132416 100644 --- "a/docs/dataStructures-algorithms/data-structure/\345\240\206.md" +++ "b/docs/cs-basics/data-structure/\345\240\206.md" @@ -1,4 +1,7 @@ +# 堆 + ## 什么是堆 + 堆是一种满足以下条件的树: 堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。或者说,任意一个节点的值都大于等于(或小于等于)所有子节点的值。 diff --git "a/docs/cs-basics/data-structure/\346\240\221.md" "b/docs/cs-basics/data-structure/\346\240\221.md" new file mode 100644 index 00000000000..010cff991ce --- /dev/null +++ "b/docs/cs-basics/data-structure/\346\240\221.md" @@ -0,0 +1,174 @@ +# 树 + +树就是一种类似现实生活中的树的数据结构(倒置的树)。任何一颗非空树只有一个根节点。 + +一棵树具有以下特点: + +1. 一棵树中的任意两个结点有且仅有唯一的一条路径连通。 +2. 一棵树如果有 n 个结点,那么它一定恰好有 n-1 条边。 +3. 一棵树不包含回路。 + +下图就是一颗树,并且是一颗二叉树。 + +![二叉树](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/二叉树-2.png) + +如上图所示,通过上面这张图说明一下树中的常用概念: + +- **节点** :树中的每个元素都可以统称为节点。 +- **根节点** :顶层节点或者说没有父节点的节点。上图中 A 节点就是根节点。 +- **父节点** :若一个节点含有子节点,则这个节点称为其子节点的父节点。上图中的 B 节点是 D 节点、E 节点的父节点。 +- **子节点** :一个节点含有的子树的根节点称为该节点的子节点。上图中 D 节点、E 节点是 B 节点的子节点。 +- **兄弟节点** :具有相同父节点的节点互称为兄弟节点。上图中 D 节点、E 节点的共同父节点是 B 节点,故 D 和 E 为兄弟节点。 +- **叶子节点** :没有子节点的节点。上图中的 D、F、H、I 都是叶子节点。 +- **节点的高度** :该节点到叶子节点的最长路径所包含的边数。 +- **节点的深度** :根节点到该节点的路径所包含的边数 +- **节点的层数** :节点的深度+1。 +- **树的高度** :根节点的高度。 + +## 二叉树的分类 + +**二叉树**(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。 + +**二叉树** 的分支通常被称作“**左子树**”或“**右子树**”。并且,**二叉树** 的分支具有左右次序,不能随意颠倒。 + +**二叉树** 的第 i 层至多拥有 `2^(i-1)` 个节点,深度为 k 的二叉树至多总共有 `2^k-1` 个节点 + +### 满二叉树 + +一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是 **满二叉树**。也就是说,如果一个二叉树的层数为 K,且结点总数是(2^k) -1 ,则它就是 **满二叉树**。如下图所示: + +![](./pictures/树/满二叉树.png) + +### 完全二叉树 + +除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则这个二叉树就是 **完全二叉树** 。 + +大家可以想象为一棵树从根结点开始扩展,扩展完左子节点才能开始扩展右子节点,每扩展完一层,才能继续扩展下一层。如下图所示: + +![](./pictures/树/完全二叉树.png) + +完全二叉树有一个很好的性质:**父结点和子节点的序号有着对应关系。** + +细心的小伙伴可能发现了,当根节点的值为 1 的情况下,若父结点的序号是 i,那么左子节点的序号就是 2i,右子节点的序号是 2i+1。这个性质使得完全二叉树利用数组存储时可以极大地节省空间,以及利用序号找到某个节点的父结点和子节点,后续二叉树的存储会详细介绍。 + +### 平衡二叉树 + +**平衡二叉树** 是一棵二叉排序树,且具有以下性质: + +1. 可以是一棵空树 +2. 如果不是空树,它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。 + +平衡二叉树的常用实现方法有 **红黑树**、**AVL 树**、**替罪羊树**、**加权平衡树**、**伸展树** 等。 + +在给大家展示平衡二叉树之前,先给大家看一棵树: + +![](./pictures/树/斜树.png) + +**你管这玩意儿叫树???** + +没错,这玩意儿还真叫树,只不过这棵树已经退化为一个链表了,我们管它叫 **斜树**。 + +**如果这样,那我为啥不直接用链表呢?** + +谁说不是呢? + +二叉树相比于链表,由于父子节点以及兄弟节点之间往往具有某种特殊的关系,这种关系使得我们在树中对数据进行**搜索**和**修改**时,相对于链表更加快捷便利。 + +但是,如果二叉树退化为一个链表了,那么那么树所具有的优秀性质就难以表现出来,效率也会大打折,为了避免这样的情况,我们希望每个做 “家长”(父结点) 的,都 **一碗水端平**,分给左儿子和分给右儿子的尽可能一样多,相差最多不超过一层,如下图所示: + +![](./pictures/树/平衡二叉树.png) + +## 二叉树的存储 + +二叉树的存储主要分为 **链式存储** 和 **顺序存储** 两种: + +### 链式存储 + +和链表类似,二叉树的链式存储依靠指针将各个节点串联起来,不需要连续的存储空间。 + +每个节点包括三个属性: + +- 数据 data。data 不一定是单一的数据,根据不同情况,可以是多个具有不同类型的数据。 +- 左节点指针 left +- 右节点指针 right。 + +可是 JAVA 没有指针啊! + +那就直接引用对象呗(别问我对象哪里找) + +![](./pictures/树/链式存储二叉树.png) + +### 顺序存储 + +顺序存储就是利用数组进行存储,数组中的每一个位置仅存储节点的 data,不存储左右子节点的指针,子节点的索引通过数组下标完成。根结点的序号为 1,对于每个节点 Node,假设它存储在数组中下标为 i 的位置,那么它的左子节点就存储在 2 _ i 的位置,它的右子节点存储在下标为 2 _ i+1 的位置。 + +一棵完全二叉树的数组顺序存储如下图所示: + +![](./pictures/树/顺序存储.png) + +大家可以试着填写一下存储如下二叉树的数组,比较一下和完全二叉树的顺序存储有何区别: + +![](./pictures/树/顺序存储2.png) + +可以看到,如果我们要存储的二叉树不是完全二叉树,在数组中就会出现空隙,导致内存利用率降低 + +## 二叉树的遍历 + +### 先序遍历 + +![](./pictures/树/先序遍历.png) + +二叉树的先序遍历,就是先输出根结点,再遍历左子树,最后遍历右子树,遍历左子树和右子树的时候,同样遵循先序遍历的规则,也就是说,我们可以递归实现先序遍历。 + +代码如下: + +```java +public void preOrder(TreeNode root){ + if(root == null){ + return; + } + system.out.println(root.data); + preOrder(root.left); + preOrder(root.right); +} +``` + +### 中序遍历 + +![](./pictures/树/中序遍历.png) + +二叉树的中序遍历,就是先递归中序遍历左子树,再输出根结点的值,再递归中序遍历右子树,大家可以想象成一巴掌把树压扁,父结点被拍到了左子节点和右子节点的中间,如下图所示: + +![](./pictures/树/中序遍历2.png) + +代码如下: + +```java +public void inOrder(TreeNode root){ + if(root == null){ + return; + } + inOrder(root.left); + system.out.println(root.data); + inOrder(root.right); +} +``` + +### 后序遍历 + +![](./pictures/树/后序遍历.png) + +二叉树的后序遍历,就是先递归后序遍历左子树,再递归后序遍历右子树,最后输出根结点的值 + +代码如下: + +```java +public void postOrder(TreeNode root){ + if(root == null){ + return; + } + postOrder(root.left); + postOrder(root.right); + system.out.println(root.data); +} +``` \ No newline at end of file diff --git "a/docs/cs-basics/data-structure/\347\272\242\351\273\221\346\240\221.md" "b/docs/cs-basics/data-structure/\347\272\242\351\273\221\346\240\221.md" new file mode 100644 index 00000000000..1f13744fa56 --- /dev/null +++ "b/docs/cs-basics/data-structure/\347\272\242\351\273\221\346\240\221.md" @@ -0,0 +1,14 @@ +**红黑树特点** : + +1. 每个节点非红即黑; +2. 根节点总是黑色的; +3. 每个叶子节点都是黑色的空节点(NIL节点); +4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定); +5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。 + +**红黑树的应用** :TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。 + +**为什么要用红黑树?** 简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐) + +**相关阅读** :[《红黑树深入剖析及Java实现》](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队) + diff --git "a/docs/dataStructures-algorithms/data-structure/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/docs/cs-basics/data-structure/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204.md" similarity index 100% rename from "docs/dataStructures-algorithms/data-structure/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204.md" rename to "docs/cs-basics/data-structure/\347\272\277\346\200\247\346\225\260\346\215\256\347\273\223\346\236\204.md" diff --git "a/docs/network/HTTPS\344\270\255\347\232\204TLS.md" "b/docs/cs-basics/network/HTTPS\344\270\255\347\232\204TLS.md" similarity index 100% rename from "docs/network/HTTPS\344\270\255\347\232\204TLS.md" rename to "docs/cs-basics/network/HTTPS\344\270\255\347\232\204TLS.md" diff --git a/docs/network/images/Cut-Trough-Switching_0.gif b/docs/cs-basics/network/images/Cut-Trough-Switching_0.gif similarity index 100% rename from docs/network/images/Cut-Trough-Switching_0.gif rename to docs/cs-basics/network/images/Cut-Trough-Switching_0.gif diff --git a/docs/network/images/isp.png b/docs/cs-basics/network/images/isp.png similarity index 100% rename from docs/network/images/isp.png rename to docs/cs-basics/network/images/isp.png diff --git "a/docs/network/images/\344\270\203\345\261\202\344\275\223\347\263\273\347\273\223\346\236\204\345\233\276.png" "b/docs/cs-basics/network/images/\344\270\203\345\261\202\344\275\223\347\263\273\347\273\223\346\236\204\345\233\276.png" similarity index 100% rename from "docs/network/images/\344\270\203\345\261\202\344\275\223\347\263\273\347\273\223\346\236\204\345\233\276.png" rename to "docs/cs-basics/network/images/\344\270\203\345\261\202\344\275\223\347\263\273\347\273\223\346\236\204\345\233\276.png" diff --git "a/docs/network/images/\344\274\240\350\276\223\345\261\202.png" "b/docs/cs-basics/network/images/\344\274\240\350\276\223\345\261\202.png" similarity index 100% rename from "docs/network/images/\344\274\240\350\276\223\345\261\202.png" rename to "docs/cs-basics/network/images/\344\274\240\350\276\223\345\261\202.png" diff --git "a/docs/network/images/\345\272\224\347\224\250\345\261\202.png" "b/docs/cs-basics/network/images/\345\272\224\347\224\250\345\261\202.png" similarity index 100% rename from "docs/network/images/\345\272\224\347\224\250\345\261\202.png" rename to "docs/cs-basics/network/images/\345\272\224\347\224\250\345\261\202.png" diff --git "a/docs/network/images/\346\225\260\346\215\256\351\223\276\350\267\257\345\261\202.png" "b/docs/cs-basics/network/images/\346\225\260\346\215\256\351\223\276\350\267\257\345\261\202.png" similarity index 100% rename from "docs/network/images/\346\225\260\346\215\256\351\223\276\350\267\257\345\261\202.png" rename to "docs/cs-basics/network/images/\346\225\260\346\215\256\351\223\276\350\267\257\345\261\202.png" diff --git "a/docs/network/images/\347\211\251\347\220\206\345\261\202.png" "b/docs/cs-basics/network/images/\347\211\251\347\220\206\345\261\202.png" similarity index 100% rename from "docs/network/images/\347\211\251\347\220\206\345\261\202.png" rename to "docs/cs-basics/network/images/\347\211\251\347\220\206\345\261\202.png" diff --git "a/docs/network/images/\347\275\221\347\273\234\345\261\202.png" "b/docs/cs-basics/network/images/\347\275\221\347\273\234\345\261\202.png" similarity index 100% rename from "docs/network/images/\347\275\221\347\273\234\345\261\202.png" rename to "docs/cs-basics/network/images/\347\275\221\347\273\234\345\261\202.png" diff --git "a/docs/network/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/\344\270\207\347\273\264\347\275\221\347\232\204\345\244\247\350\207\264\345\267\245\344\275\234\345\267\245\347\250\213.png" "b/docs/cs-basics/network/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/\344\270\207\347\273\264\347\275\221\347\232\204\345\244\247\350\207\264\345\267\245\344\275\234\345\267\245\347\250\213.png" similarity index 100% rename from "docs/network/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/\344\270\207\347\273\264\347\275\221\347\232\204\345\244\247\350\207\264\345\267\245\344\275\234\345\267\245\347\250\213.png" rename to "docs/cs-basics/network/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\347\202\271\346\200\273\347\273\223/\344\270\207\347\273\264\347\275\221\347\232\204\345\244\247\350\207\264\345\267\245\344\275\234\345\267\245\347\250\213.png" diff --git "a/docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" "b/docs/cs-basics/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" similarity index 100% rename from "docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" rename to "docs/cs-basics/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" diff --git "a/docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md" "b/docs/cs-basics/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md" similarity index 100% rename from "docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md" rename to "docs/cs-basics/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md" diff --git a/docs/operating-system/Shell.md b/docs/cs-basics/operating-system/Shell.md similarity index 100% rename from docs/operating-system/Shell.md rename to docs/cs-basics/operating-system/Shell.md diff --git a/docs/operating-system/basis.md b/docs/cs-basics/operating-system/basis.md similarity index 100% rename from docs/operating-system/basis.md rename to docs/cs-basics/operating-system/basis.md diff --git a/docs/operating-system/images/Linux-Logo.png b/docs/cs-basics/operating-system/images/Linux-Logo.png similarity index 100% rename from docs/operating-system/images/Linux-Logo.png rename to docs/cs-basics/operating-system/images/Linux-Logo.png diff --git "a/docs/operating-system/images/Linux\344\271\213\347\210\266.png" "b/docs/cs-basics/operating-system/images/Linux\344\271\213\347\210\266.png" similarity index 100% rename from "docs/operating-system/images/Linux\344\271\213\347\210\266.png" rename to "docs/cs-basics/operating-system/images/Linux\344\271\213\347\210\266.png" diff --git "a/docs/operating-system/images/Linux\346\235\203\351\231\220\345\221\275\344\273\244.png" "b/docs/cs-basics/operating-system/images/Linux\346\235\203\351\231\220\345\221\275\344\273\244.png" similarity index 100% rename from "docs/operating-system/images/Linux\346\235\203\351\231\220\345\221\275\344\273\244.png" rename to "docs/cs-basics/operating-system/images/Linux\346\235\203\351\231\220\345\221\275\344\273\244.png" diff --git "a/docs/operating-system/images/Linux\346\235\203\351\231\220\350\247\243\350\257\273.png" "b/docs/cs-basics/operating-system/images/Linux\346\235\203\351\231\220\350\247\243\350\257\273.png" similarity index 100% rename from "docs/operating-system/images/Linux\346\235\203\351\231\220\350\247\243\350\257\273.png" rename to "docs/cs-basics/operating-system/images/Linux\346\235\203\351\231\220\350\247\243\350\257\273.png" diff --git "a/docs/operating-system/images/Linux\347\233\256\345\275\225\346\240\221.png" "b/docs/cs-basics/operating-system/images/Linux\347\233\256\345\275\225\346\240\221.png" similarity index 100% rename from "docs/operating-system/images/Linux\347\233\256\345\275\225\346\240\221.png" rename to "docs/cs-basics/operating-system/images/Linux\347\233\256\345\275\225\346\240\221.png" diff --git a/docs/operating-system/images/linux.png b/docs/cs-basics/operating-system/images/linux.png similarity index 100% rename from docs/operating-system/images/linux.png rename to docs/cs-basics/operating-system/images/linux.png diff --git a/docs/operating-system/images/macos.png b/docs/cs-basics/operating-system/images/macos.png similarity index 100% rename from docs/operating-system/images/macos.png rename to docs/cs-basics/operating-system/images/macos.png diff --git a/docs/operating-system/images/unix.png b/docs/cs-basics/operating-system/images/unix.png similarity index 100% rename from docs/operating-system/images/unix.png rename to docs/cs-basics/operating-system/images/unix.png diff --git a/docs/operating-system/images/windows.png b/docs/cs-basics/operating-system/images/windows.png similarity index 100% rename from docs/operating-system/images/windows.png rename to docs/cs-basics/operating-system/images/windows.png diff --git "a/docs/operating-system/images/\344\277\256\346\224\271\346\226\207\344\273\266\346\235\203\351\231\220.png" "b/docs/cs-basics/operating-system/images/\344\277\256\346\224\271\346\226\207\344\273\266\346\235\203\351\231\220.png" similarity index 100% rename from "docs/operating-system/images/\344\277\256\346\224\271\346\226\207\344\273\266\346\235\203\351\231\220.png" rename to "docs/cs-basics/operating-system/images/\344\277\256\346\224\271\346\226\207\344\273\266\346\235\203\351\231\220.png" diff --git "a/docs/operating-system/images/\346\226\207\344\273\266inode\344\277\241\346\201\257.png" "b/docs/cs-basics/operating-system/images/\346\226\207\344\273\266inode\344\277\241\346\201\257.png" similarity index 100% rename from "docs/operating-system/images/\346\226\207\344\273\266inode\344\277\241\346\201\257.png" rename to "docs/cs-basics/operating-system/images/\346\226\207\344\273\266inode\344\277\241\346\201\257.png" diff --git "a/docs/operating-system/images/\347\224\250\346\210\267\346\200\201\344\270\216\345\206\205\346\240\270\346\200\201.png" "b/docs/cs-basics/operating-system/images/\347\224\250\346\210\267\346\200\201\344\270\216\345\206\205\346\240\270\346\200\201.png" similarity index 100% rename from "docs/operating-system/images/\347\224\250\346\210\267\346\200\201\344\270\216\345\206\205\346\240\270\346\200\201.png" rename to "docs/cs-basics/operating-system/images/\347\224\250\346\210\267\346\200\201\344\270\216\345\206\205\346\240\270\346\200\201.png" diff --git a/docs/operating-system/linux.md b/docs/cs-basics/operating-system/linux.md similarity index 100% rename from docs/operating-system/linux.md rename to docs/cs-basics/operating-system/linux.md diff --git "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" deleted file mode 100644 index 5caffaac901..00000000000 --- "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ /dev/null @@ -1,179 +0,0 @@ -> 注意!!!这部分内容会进行重构,以下内容仅作为参考。 -> - -- [Queue](#queue) - - [什么是队列](#什么是队列) - - [队列的种类](#队列的种类) - - [Java 集合框架中的队列 Queue](#java-集合框架中的队列-queue) - - [推荐文章](#推荐文章) -- [Set](#set) - - [什么是 Set](#什么是-set) - - [补充:有序集合与无序集合说明](#补充:有序集合与无序集合说明) - - [HashSet 和 TreeSet 底层数据结构](#hashset-和-treeset-底层数据结构) - - [推荐文章](#推荐文章-1) -- [List](#list) - - [什么是List](#什么是list) - - [List的常见实现类](#list的常见实现类) - - [ArrayList 和 LinkedList 源码学习](#arraylist-和-linkedlist-源码学习) - - [推荐阅读](#推荐阅读) -- [Map](#map) -- [树](#树) - - - - -## Queue - -### 什么是队列 -队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列),跟我们生活中的排队类似。 - -### 队列的种类 - -- **单队列**(单队列就是常见的队列, 每次添加元素时,都是添加到队尾,存在“假溢出”的问题也就是明明有位置却不能添加的情况) -- **循环队列**(避免了“假溢出”的问题) - -### Java 集合框架中的队列 Queue - -Java 集合中的 Queue 继承自 Collection 接口 ,Deque, LinkedList, PriorityQueue, BlockingQueue 等类都实现了它。 -Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问。 -除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作。 - -## Set - -### 什么是 Set -Set 继承于 Collection 接口,是一个不允许出现重复元素,并且无序的集合,主要 HashSet 和 TreeSet 两大实现类。 - -在判断重复元素的时候,HashSet 集合会调用 hashCode()和 equal()方法来实现;TreeSet 集合会调用compareTo方法来实现。 - -### 补充:有序集合与无序集合说明 -- 有序集合:集合里的元素可以根据 key 或 index 访问 (List、Map) -- 无序集合:集合里的元素只能遍历。(Set) - - -### HashSet 和 TreeSet 底层数据结构 - -**HashSet** 是哈希表结构,主要利用 HashMap 的 key 来存储元素,计算插入元素的 hashCode 来获取元素在集合中的位置; - -**TreeSet** 是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序; - -## List - -### 什么是List - -在 List 中,用户可以精确控制列表中每个元素的插入位置,另外用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。 与 Set 不同,List 通常允许重复的元素。 另外 List 是有序集合而 Set 是无序集合。 - -### List的常见实现类 - -**ArrayList** 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。 - -**LinkedList** 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率高。 - -**Vector** 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。 - -**Stack** 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。 - -## 树 - -### 1 二叉树 - -[二叉树](https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科) - -(1)[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。 - -(2)[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。 - -(3)[平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/10421057)——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 - -### 2 完全二叉树 - -[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科) - -完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。 - -### 3 满二叉树 - -[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,国内外的定义不同) - -国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。 - -### 堆 - -[数据结构之堆的定义](https://blog.csdn.net/qq_33186366/article/details/51876191) - -堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。 - -### 4 二叉查找树(BST) - -[浅谈算法和数据结构: 七 二叉查找树](https://www.yycoding.xyz/post/2014/3/24/introduce-binary-search-tree) - -二叉查找树的特点: - -1. 若任意节点的左子树不空,则左子树上所有结点的 值均小于它的根结点的值; -2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值; -3. 任意节点的左、右子树也分别为二叉查找树; -4. 没有键值相等的节点(no duplicate nodes)。 - -### 5 平衡二叉树(Self-balancing binary search tree) - -[ 平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等) - -### 6 红黑树 - -红黑树特点: - -1. 每个节点非红即黑; -2. 根节点总是黑色的; -3. 每个叶子节点都是黑色的空节点(NIL节点); -4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定); -5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。 - - -红黑树的应用: - -TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。 - -**为什么要用红黑树?** - - -简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐) - -推荐文章: - -- [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐) -- [寻找红黑树的操作手册](http://dandanlove.com/2018/03/18/red-black-tree/)(文章排版以及思路真的不错) -- [红黑树深入剖析及Java实现](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队) - -### 7 B-,B+,B*树 - -[二叉树学习笔记之B树、B+树、B*树 ](https://yq.aliyun.com/articles/38345) - -[《B-树,B+树,B*树详解》](https://blog.csdn.net/aqzwss/article/details/53074186) - -[《B-树,B+树与B*树的优缺点比较》](https://blog.csdn.net/bigtree_3721/article/details/73632405) - -B-树(或B树)是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance) - -1. B+ 树的叶子节点链表结构相比于 B- 树便于扫库,和范围检索。 -2. B+树支持range-query(区间查询)非常方便,而B树不支持。这是数据库选用B+树的最主要原因。 -3. B\*树 是B+树的变体,B\*树分配新结点的概率比B+树要低,空间使用率更高; - -### 8 LSM 树 - -[[HBase] LSM树 VS B+树](https://blog.csdn.net/dbanote/article/details/8897599) - -B+树最大的性能问题是会产生大量的随机IO - -为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。 - -[LSM树由来、设计思想以及应用到HBase的索引](https://www.cnblogs.com/yanghuahui/p/3483754.html) - - -## 图 - - - - -## BFS及DFS - -- [《使用BFS及DFS遍历树和图的思路及实现》](https://blog.csdn.net/Gene1994/article/details/85097507) - diff --git a/docs/database/Redis/redis-all.md b/docs/database/Redis/redis-all.md index 64ea49d563a..93227374d2e 100644 --- a/docs/database/Redis/redis-all.md +++ b/docs/database/Redis/redis-all.md @@ -1,45 +1,5 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。 - - - - - -- [1. 简单介绍一下 Redis 呗!](#1-简单介绍一下-redis-呗) -- [2. 分布式缓存常见的技术选型方案有哪些?](#2-分布式缓存常见的技术选型方案有哪些) -- [3. 说一下 Redis 和 Memcached 的区别和共同点](#3-说一下-redis-和-memcached-的区别和共同点) -- [4. 缓存数据的处理流程是怎样的?](#4-缓存数据的处理流程是怎样的) -- [5. 为什么要用 Redis/为什么要用缓存?](#5-为什么要用-redis为什么要用缓存) -- [6. Redis 常见数据结构以及使用场景分析](#6-redis-常见数据结构以及使用场景分析) - - [6.1. string](#61-string) - - [6.2. list](#62-list) - - [6.3. hash](#63-hash) - - [6.4. set](#64-set) - - [6.5. sorted set](#65-sorted-set) - - [6.6 bitmap](#66-bitmap) -- [7. Redis 单线程模型详解](#7-redis-单线程模型详解) -- [8. Redis 没有使用多线程?为什么不使用多线程?](#8-redis-没有使用多线程为什么不使用多线程) -- [9. Redis6.0 之后为何引入了多线程?](#9-redis60-之后为何引入了多线程) -- [10. Redis 给缓存数据设置过期时间有啥用?](#10-redis-给缓存数据设置过期时间有啥用) -- [11. Redis 是如何判断数据是否过期的呢?](#11-redis-是如何判断数据是否过期的呢) -- [12. 过期的数据的删除策略了解么?](#12-过期的数据的删除策略了解么) -- [13. Redis 内存淘汰机制了解么?](#13-redis-内存淘汰机制了解么) -- [14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)](#14-redis-持久化机制怎么保证-redis-挂掉之后再重启数据可以进行恢复) -- [15. Redis 事务](#15-redis-事务) -- [16. 缓存穿透](#16-缓存穿透) - - [16.1. 什么是缓存穿透?](#161-什么是缓存穿透) - - [16.2. 缓存穿透情况的处理流程是怎样的?](#162-缓存穿透情况的处理流程是怎样的) - - [16.3. 有哪些解决办法?](#163-有哪些解决办法) -- [17. 缓存雪崩](#17-缓存雪崩) - - [17.1. 什么是缓存雪崩?](#171-什么是缓存雪崩) - - [17.2. 有哪些解决办法?](#172-有哪些解决办法) -- [18. 如何保证缓存和数据库数据的一致性?](#18-如何保证缓存和数据库数据的一致性) -- [19. 参考](#19-参考) -- [20. 公众号](#20-公众号) - - - - -### 1. 简单介绍一下 Redis 呗! + +### 简单介绍一下 Redis 呗! 简单来说 **Redis 就是一个使用 C 语言开发的数据库**,不过与传统数据库不同的是 **Redis 的数据是存在内存中的** ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。 @@ -47,7 +7,7 @@ **Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。** -### 2. 分布式缓存常见的技术选型方案有哪些? +### 分布式缓存常见的技术选型方案有哪些? 分布式缓存的话,使用的比较多的主要是 **Memcached** 和 **Redis**。不过,现在基本没有看过还有项目使用 **Memcached** 来做缓存,都是直接用 **Redis**。 @@ -55,7 +15,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来 分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用信息的问题。因为,本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共同的。 -### 3. 说一下 Redis 和 Memcached 的区别和共同点 +### 说一下 Redis 和 Memcached 的区别和共同点 现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!不过,了解 Redis 和 Memcached 的区别和共同点,有助于我们在做相应的技术选型的时候,能够做到有理有据! @@ -78,7 +38,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来 相信看了上面的对比之后,我们已经没有什么理由可以选择使用 Memcached 来作为自己项目的分布式缓存了。 -### 4. 缓存数据的处理流程是怎样的? +### 缓存数据的处理流程是怎样的? 作为暖男一号,我给大家画了一个草图。 @@ -91,7 +51,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来 3. 数据库中存在的话就更新缓存中的数据。 4. 数据库中不存在的话就返回空数据。 -### 5. 为什么要用 Redis/为什么要用缓存? +### 为什么要用 Redis/为什么要用缓存? _简单,来说使用缓存主要是为了提升用户体验以及应对更多的用户。_ @@ -117,13 +77,21 @@ _简单,来说使用缓存主要是为了提升用户体验以及应对更多 由此可见,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。 -### 6. Redis 常见数据结构以及使用场景分析 +### Redis 除了做缓存,还能做什么? + +- **分布式锁** : 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。相关阅读:[《分布式锁中的王者方案 - Redisson》](https://mp.weixin.qq.com/s/CbnPRfvq4m1sqo2uKI6qQw)。 +- **限流** :一般是通过 Redis + Lua 脚本的方式来实现限流。相关阅读:[《我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了!》](https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA)。 +- **消息队列** :Redis 自带的 list 数据结构可以作为一个简单的队列使用。Redis5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。 +- **复杂业务场景** :通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 bitmap 统计活跃用户、通过 sorted set 维护排行榜。 +- ...... + +### Redis 常见数据结构以及使用场景分析 你可以自己本机安装 redis 或者通过 redis 官网提供的[在线 redis 环境](https://try.redis.io/)。 ![try-redis](./images/redis-all/try-redis.png) -#### 6.1. string +#### string 1. **介绍** :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**(simple dynamic string,**SDS**)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。 2. **常用命令:** `set,get,strlen,exists,decr,incr,setex` 等等。 @@ -184,7 +152,7 @@ OK (integer) 56 ``` -#### 6.2. list +#### list 1. **介绍** :**list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 2. **常用命令:** `rpush,lpop,lpush,rpop,lrange,llen` 等。 @@ -245,7 +213,7 @@ OK (integer) 3 ``` -#### 6.3. hash +#### hash 1. **介绍** :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表,**特别适合用于存储对象**,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。 2. **常用命令:** `hset,hmset,hexists,hget,hgetall,hkeys,hvals` 等。 @@ -282,7 +250,7 @@ OK "GuideGeGe" ``` -#### 6.4. set +#### set 1. **介绍 :** set 类似于 Java 中的 `HashSet` 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。 2. **常用命令:** `sadd,spop,smembers,sismember,scard,sinterstore,sunion` 等。 @@ -310,7 +278,7 @@ OK 1) "value2" ``` -#### 6.5. sorted set +#### sorted set 1. **介绍:** 和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。 2. **常用命令:** `zadd,zcard,zscore,zrange,zrevrange,zrem` 等。 @@ -337,7 +305,7 @@ OK 2) "value2" ``` -#### 6.6 bitmap +#### bitmap 1. **介绍:** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。 2. **常用命令:** `setbit` 、`getbit` 、`bitcount`、`bitop` @@ -417,7 +385,7 @@ BITOP operation destkey key [key ...] 只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。 -### 7. Redis 单线程模型详解 +### Redis 单线程模型详解 **Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型** (Netty 的线程模型也基于 Reactor 模式,Reactor 模式不愧是高性能 IO 的基石),这套事件处理模型对应的是 Redis 中的文件事件处理器(file event handler)。由于文件事件处理器(file event handler)是单线程方式运行的,所以我们一般都说 Redis 是单线程模型。 @@ -450,7 +418,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(

《Redis设计与实现:12章》

-### 8. Redis 没有使用多线程?为什么不使用多线程? +### Redis 没有使用多线程?为什么不使用多线程? 虽然说 Redis 是单线程模型,但是,实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。** @@ -468,7 +436,7 @@ Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接( 2. Redis 的性能瓶颈不在 CPU ,主要在内存和网络; 3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。 -### 9. Redis6.0 之后为何引入了多线程? +### Redis6.0 之后为何引入了多线程? **Redis6.0 引入多线程主要是为了提高网络 IO 读写性能**,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。 @@ -491,7 +459,7 @@ io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的 1. [Redis 6.0 新特性-多线程连环 13 问!](https://mp.weixin.qq.com/s/FZu3acwK6zrCBZQ_3HoUgw) 2. [为什么 Redis 选择单线程模型](https://draveness.me/whys-the-design-redis-single-thread/) -### 10. Redis 给缓存数据设置过期时间有啥用? +### Redis 给缓存数据设置过期时间有啥用? 一般情况下,我们设置保存的缓存数据的时候都会设置一个过期时间。为什么呢? @@ -516,7 +484,7 @@ OK 如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多。 -### 11. Redis 是如何判断数据是否过期的呢? +### Redis 是如何判断数据是否过期的呢? Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。 @@ -534,7 +502,7 @@ typedef struct redisDb { } redisDb; ``` -### 12. 过期的数据的删除策略了解么? +### 过期的数据的删除策略了解么? 如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢? @@ -549,7 +517,7 @@ typedef struct redisDb { 怎么解决这个问题呢?答案就是:**Redis 内存淘汰机制。** -### 13. Redis 内存淘汰机制了解么? +### Redis 内存淘汰机制了解么? > 相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据? @@ -567,7 +535,7 @@ Redis 提供 6 种数据淘汰策略: 7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰 8. **allkeys-lfu(least frequently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key -### 14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复) +### Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复) 很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。 @@ -629,7 +597,7 @@ AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。 -### 15. Redis 事务 +### Redis 事务 Redis 可以通过 **`MULTI`,`EXEC`,`DISCARD` 和 `WATCH`** 等命令来实现事务(transaction)功能。 @@ -704,19 +672,19 @@ Redis 官网也解释了自己为啥不支持回滚。简单来说就是 Redis - [issue452: 关于 Redis 事务不满足原子性的问题](https://github.com/Snailclimb/JavaGuide/issues/452) 。 - [Issue491:关于 redis 没有事务回滚?](https://github.com/Snailclimb/JavaGuide/issues/491) -### 16. 缓存穿透 +### 缓存穿透 -#### 16.1. 什么是缓存穿透? +#### 什么是缓存穿透? 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。 -#### 16.2. 缓存穿透情况的处理流程是怎样的? +#### 缓存穿透情况的处理流程是怎样的? 如下图所示,用户的请求最终都要跑到数据库中查询一遍。 ![缓存穿透情况](./images/redis-all/缓存穿透情况.png) -#### 16.3. 有哪些解决办法? +#### 有哪些解决办法? 最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。 @@ -777,9 +745,9 @@ _为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理 更多关于布隆过滤器的内容可以看我的这篇原创:[《不了解布隆过滤器?一文给你整的明明白白!》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/dataStructures-algorithms/data-structure/bloom-filter.md) ,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。 -### 17. 缓存雪崩 +### 缓存雪崩 -#### 17.1. 什么是缓存雪崩? +#### 什么是缓存雪崩? 我发现缓存雪崩这名字起的有点意思,哈哈。 @@ -791,7 +759,7 @@ _为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理 举个例子 :秒杀开始 12 个小时之前,我们统一存放了一批商品到 Redis 中,设置的缓存过期时间也是 12 个小时,那么秒杀开始的时候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩一样可怕。 -#### 17.2. 有哪些解决办法? +#### 有哪些解决办法? **针对 Redis 服务不可用的情况:** @@ -803,7 +771,7 @@ _为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理 1. 设置不同的失效时间比如随机设置缓存的失效时间。 2. 缓存永不失效。 -### 18. 如何保证缓存和数据库数据的一致性? +### 如何保证缓存和数据库数据的一致性? 细说的话可以扯很多,但是我觉得其实没太大必要(小声 BB:很多解决方案我也没太弄明白)。我个人觉得引入缓存之后,如果为了短时间的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要。 @@ -816,20 +784,10 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删 1. **缓存失效时间变短(不推荐,治标不治本)** :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。 2. **增加 cache 更新重试机制(常用)**: 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。 -### 19. 参考 +### 参考 - 《Redis 开发与运维》 - 《Redis 设计与实现》 - Redis 命令总结:http://Redisdoc.com/string/set.html - 通俗易懂的 Redis 数据结构基础教程:[https://juejin.im/post/5b53ee7e5188251aaa2d2e16](https://juejin.im/post/5b53ee7e5188251aaa2d2e16) - WHY Redis choose single thread (vs multi threads): [https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153](https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153) - -### 20. 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java 面试突击》:** 由本文档衍生的专为面试而生的《Java 面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java 面试突击"** 即可免费领取! - -**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) \ No newline at end of file diff --git "a/docs/database/mysql/MySQL\344\270\211\345\244\247\346\227\245\345\277\227(binlog\343\200\201redo log\345\222\214undo log).md" "b/docs/database/mysql/MySQL\344\270\211\345\244\247\346\227\245\345\277\227(binlog\343\200\201redo log\345\222\214undo log).md" new file mode 100644 index 00000000000..616fab71874 --- /dev/null +++ "b/docs/database/mysql/MySQL\344\270\211\345\244\247\346\227\245\345\277\227(binlog\343\200\201redo log\345\222\214undo log).md" @@ -0,0 +1,280 @@ +> 本文来自公号程序猿阿星投稿,JavaGuide 对其做了补充完善。 + +## 前言 + +`MySQL` 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 `binlog`(归档日志)和事务日志 `redo log`(重做日志)和 `undo log`(回滚日志)。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/01.png) + +今天就来聊聊 `redo log`(重做日志)、`binlog`(归档日志)、两阶段提交、`undo log` (回滚日志)。 + +## redo log + +`redo log`(重做日志)是`InnoDB`存储引擎独有的,它让`MySQL`拥有了崩溃恢复能力。 + +比如 `MySQL` 实例挂了或宕机了,重启时,`InnoDB`存储引擎会使用`redo log`恢复数据,保证数据的持久性与完整性。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/02.png) + +`MySQL` 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 `Buffer Pool` 中。 + +后续的查询都是先从 `Buffer Pool` 中找,没有命中再去硬盘加载,减少硬盘 `IO` 开销,提升性能。 + +更新表数据的时候,也是如此,发现 `Buffer Pool` 里存在要更新的数据,就直接在 `Buffer Pool` 里更新。 + +然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(`redo log buffer`)里,接着刷盘到 `redo log` 文件里。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/03.png) + +理想情况,事务一提交就会进行刷盘操作,但实际上,刷盘的时机是根据策略来进行的。 + +> 小贴士:每条 redo 记录由“表空间号+数据页号+偏移量+修改数据长度+具体修改的数据”组成 + +### 刷盘时机 + +`InnoDB` 存储引擎为 `redo log` 的刷盘策略提供了 `innodb_flush_log_at_trx_commit` 参数,它支持三种策略: + +- **0** :设置为 0 的时候,表示每次事务提交时不进行刷盘操作 +- **1** :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值) +- **2** :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache + +`innodb_flush_log_at_trx_commit` 参数默认为 1 ,也就是说当事务提交时会调用 `fsync` 对 redo log 进行刷盘 + +另外,`InnoDB` 存储引擎有一个后台线程,每隔`1` 秒,就会把 `redo log buffer` 中的内容写到文件系统缓存(`page cache`),然后调用 `fsync` 刷盘。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/04.png) + +也就是说,一个没有提交事务的 `redo log` 记录,也可能会刷盘。 + +**为什么呢?** + +因为在事务执行过程 `redo log` 记录是会写入`redo log buffer` 中,这些 `redo log` 记录会被后台线程刷盘。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/05.png) + +除了后台线程每秒`1`次的轮询操作,还有一种情况,当 `redo log buffer` 占用的空间即将达到 `innodb_log_buffer_size` 一半的时候,后台线程会主动刷盘。 + +下面是不同刷盘策略的流程图。 + +#### innodb_flush_log_at_trx_commit=0 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/06.png) + +为`0`时,如果`MySQL`挂了或宕机可能会有`1`秒数据的丢失。 + +#### innodb_flush_log_at_trx_commit=1 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/07.png) + +为`1`时, 只要事务提交成功,`redo log`记录就一定在硬盘里,不会有任何数据丢失。 + +如果事务执行期间`MySQL`挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。 + +#### innodb_flush_log_at_trx_commit=2 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/09.png) + +为`2`时, 只要事务提交成功,`redo log buffer`中的内容只写入文件系统缓存(`page cache`)。 + +如果仅仅只是`MySQL`挂了不会有任何数据丢失,但是宕机可能会有`1`秒数据的丢失。 + +### 日志文件组 + +硬盘上存储的 `redo log` 日志文件不只一个,而是以一个**日志文件组**的形式出现的,每个的`redo`日志文件大小都是一样的。 + +比如可以配置为一组`4`个文件,每个文件的大小是 `1GB`,整个 `redo log` 日志文件组可以记录`4G`的内容。 + +它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/10.png) + +在个**日志文件组**中还有两个重要的属性,分别是 `write pos、checkpoint` + +- **write pos** 是当前记录的位置,一边写一边后移 +- **checkpoint** 是当前要擦除的位置,也是往后推移 + +每次刷盘 `redo log` 记录到**日志文件组**中,`write pos` 位置就会后移更新。 + +每次 `MySQL` 加载**日志文件组**恢复数据时,会清空加载过的 `redo log` 记录,并把 `checkpoint` 后移更新。 + +`write pos` 和 `checkpoint` 之间的还空着的部分可以用来写入新的 `redo log` 记录。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/11.png) + +如果 `write pos` 追上 `checkpoint` ,表示**日志文件组**满了,这时候不能再写入新的 `redo log` 记录,`MySQL` 得停下来,清空一些记录,把 `checkpoint` 推进一下。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/03/12.png) + +### redo log 小结 + +相信大家都知道 `redo log` 的作用和它的刷盘时机、存储形式。 + +现在我们来思考一个问题: **只要每次把修改后的数据页直接刷盘不就好了,还有 `redo log` 什么事?** + +它们不都是刷盘么?差别在哪里? + +```java +1 Byte = 8bit +1 KB = 1024 Byte +1 MB = 1024 KB +1 GB = 1024 MB +1 TB = 1024 GB +``` + +实际上,数据页大小是`16KB`,刷盘比较耗时,可能就修改了数据页里的几 `Byte` 数据,有必要把完整的数据页刷盘吗? + +而且数据页刷盘是随机写,因为一个数据页对应的位置可能在硬盘文件的随机位置,所以性能是很差。 + +如果是写 `redo log`,一行记录可能就占几十 `Byte`,只包含表空间号、数据页号、磁盘文件偏移 +量、更新值,再加上是顺序写,所以刷盘速度很快。 + +所以用 `redo log` 形式记录修改内容,性能会远远超过刷数据页的方式,这也让数据库的并发能力更强。 + +> 其实内存的数据页在一定时机也会刷盘,我们把这称为页合并,讲 `Buffer Pool`的时候会对这块细说 + +## binlog + +`redo log` 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 `InnoDB` 存储引擎。 + +而 `binlog` 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于`MySQL Server` 层。 + +不管用什么存储引擎,只要发生了表数据更新,都会产生 `binlog` 日志。 + +那 `binlog` 到底是用来干嘛的? + +可以说`MySQL`数据库的**数据备份、主备、主主、主从**都离不开`binlog`,需要依靠`binlog`来同步数据,保证数据一致性。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/01.png) + +`binlog`会记录所有涉及更新数据的逻辑操作,并且是顺序写。 + +### 记录格式 + +`binlog` 日志有三种格式,可以通过`binlog_format`参数指定。 + +- **statement** +- **row** +- **mixed** + +指定`statement`,记录的内容是`SQL`语句原文,比如执行一条`update T set update_time=now() where id=1`,记录的内容如下。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/02.png) + +同步数据时,会执行记录的`SQL`语句,但是有个问题,`update_time=now()`这里会获取当前系统时间,直接执行会导致与原库的数据不一致。 + +为了解决这种问题,我们需要指定为`row`,记录的内容不再是简单的`SQL`语句了,还包含操作的具体数据,记录内容如下。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/03.png) + +`row`格式记录的内容看不到详细信息,要通过`mysqlbinlog`工具解析出来。 + +`update_time=now()`变成了具体的时间`update_time=1627112756247`,条件后面的@1、@2、@3 都是该行数据第 1 个~3 个字段的原始值(**假设这张表只有 3 个字段**)。 + +这样就能保证同步数据的一致性,通常情况下都是指定为`row`,这样可以为数据库的恢复与同步带来更好的可靠性。 + +但是这种格式,需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗`IO`资源,影响执行速度。 + +所以就有了一种折中的方案,指定为`mixed`,记录的内容是前两者的混合。 + +`MySQL`会判断这条`SQL`语句是否可能引起数据不一致,如果是,就用`row`格式,否则就用`statement`格式。 + +### 写入机制 + +`binlog`的写入时机也非常简单,事务执行过程中,先把日志写到`binlog cache`,事务提交的时候,再把`binlog cache`写到`binlog`文件中。 + +因为一个事务的`binlog`不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为`binlog cache`。 + +我们可以通过`binlog_cache_size`参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(`Swap`)。 + +`binlog`日志刷盘流程如下 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/04.png) + +- **上图的 write,是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快** +- **上图的 fsync,才是将数据持久化到磁盘的操作** + +`write`和`fsync`的时机,可以由参数`sync_binlog`控制,默认是`0`。 + +为`0`的时候,表示每次提交事务都只`write`,由系统自行判断什么时候执行`fsync`。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/05.png) + +虽然性能得到提升,但是机器宕机,`page cache`里面的 binglog 会丢失。 + +为了安全起见,可以设置为`1`,表示每次提交事务都会执行`fsync`,就如同**binlog 日志刷盘流程**一样。 + +最后还有一种折中方式,可以设置为`N(N>1)`,表示每次提交事务都`write`,但累积`N`个事务后才`fsync`。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/04/06.png) + +在出现`IO`瓶颈的场景里,将`sync_binlog`设置成一个比较大的值,可以提升性能。 + +同样的,如果机器宕机,会丢失最近`N`个事务的`binlog`日志。 + +## 两阶段提交 + +`redo log`(重做日志)让`InnoDB`存储引擎拥有了崩溃恢复能力。 + +`binlog`(归档日志)保证了`MySQL`集群架构的数据一致性。 + +虽然它们都属于持久化的保证,但是则重点不同。 + +在执行更新语句过程,会记录`redo log`与`binlog`两块日志,以基本的事务为单位,`redo log`在事务执行过程中可以不断写入,而`binlog`只有在提交事务时才写入,所以`redo log`与`binlog`的写入时机不一样。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/01.png) + +回到正题,`redo log`与`binlog`两份日志之间的逻辑不一致,会出现什么问题? + +我们以`update`语句为例,假设`id=2`的记录,字段`c`值是`0`,把字段`c`值更新成`1`,`SQL`语句为`update T set c=1 where id=2`。 + +假设执行过程中写完`redo log`日志后,`binlog`日志写期间发生了异常,会出现什么情况呢? + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/02.png) + +由于`binlog`没写完就异常,这时候`binlog`里面没有对应的修改记录。因此,之后用`binlog`日志恢复数据时,就会少这一次更新,恢复出来的这一行`c`值是`0`,而原库因为`redo log`日志恢复,这一行`c`值是`1`,最终数据不一致。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/03.png) + +为了解决两份日志之间的逻辑一致问题,`InnoDB`存储引擎使用**两阶段提交**方案。 + +原理很简单,将`redo log`的写入拆成了两个步骤`prepare`和`commit`,这就是**两阶段提交**。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/04.png) + +使用**两阶段提交**后,写入`binlog`时发生异常也不会有影响,因为`MySQL`根据`redo log`日志恢复数据时,发现`redo log`还处于`prepare`阶段,并且没有对应`binlog`日志,就会回滚该事务。 + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/05.png) + +再看一个场景,`redo log`设置`commit`阶段发生异常,那会不会回滚事务呢? + +![](https://cdn.jsdelivr.net/gh/18702524676/CND5/image/mysql/05/06.png) + +并不会回滚事务,它会执行上图框住的逻辑,虽然`redo log`是处于`prepare`阶段,但是能通过事务`id`找到对应的`binlog`日志,所以`MySQL`认为是完整的,就会提交事务恢复数据。 + +## undo log + +> 这部分内容为 JavaGuide 的补充: + +我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行**回滚**,在 MySQL 中,恢复机制是通过 **回滚日志(undo log)** 实现的,所有事务进行的修改都会先先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 **回滚日志** 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。 + +另外,`MVCC` 的实现依赖于:**隐藏字段、Read View、undo log**。在内部实现中,`InnoDB` 通过数据行的 `DB_TRX_ID` 和 `Read View` 来判断数据的可见性,如不可见,则通过数据行的 `DB_ROLL_PTR` 找到 `undo log` 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 `Read View` 之前已经提交的修改和该事务本身做的修改 + +## 总结 + +> 这部分内容为 JavaGuide 的补充: + +MySQL InnoDB 引擎使用 **redo log(重做日志)** 保证事务的**持久性**,使用 **undo log(回滚日志)** 来保证事务的**原子性**。 + +`MySQL`数据库的**数据备份、主备、主主、主从**都离不开`binlog`,需要依靠`binlog`来同步数据,保证数据一致性。 + +## 站在巨人的肩膀上 + +- 《MySQL 实战 45 讲》 +- 《从零开始带你成为 MySQL 实战优化高手》 +- 《MySQL 是怎样运行的:从根儿上理解 MySQL》 +- 《MySQL 技术 Innodb 存储引擎》 + +## MySQL 好文推荐 + +- [CURD 这么多年,你有了解过 MySQL 的架构设计吗?](https://mp.weixin.qq.com/s/R-1km7r0z3oWfwYQV8iiqA) +- [浅谈 MySQL InnoDB 的内存组件](https://mp.weixin.qq.com/s/7Kab4IQsNcU_bZdbv_MuOg) \ No newline at end of file diff --git "a/docs/java/basis/\344\273\243\347\220\206\346\250\241\345\274\217\350\257\246\350\247\243.md" "b/docs/java/basis/\344\273\243\347\220\206\346\250\241\345\274\217\350\257\246\350\247\243.md" index 8323cedc168..704de492bef 100644 --- "a/docs/java/basis/\344\273\243\347\220\206\346\250\241\345\274\217\350\257\246\350\247\243.md" +++ "b/docs/java/basis/\344\273\243\347\220\206\346\250\241\345\274\217\350\257\246\350\247\243.md" @@ -348,7 +348,7 @@ public class DebugMethodInterceptor implements MethodInterceptor { /** - * @param o 被代理的对象(需要增强的对象) + * @param o 代理对象(增强的对象) * @param method 被拦截的方法(需要增强的方法) * @param args 方法入参 * @param methodProxy 用于调用原始方法 diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" index 5c0c5b389d7..ce99f80418e 100644 --- "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" +++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" @@ -3,11 +3,12 @@ - [1. 剖析面试最常见问题之 Java 集合框架](#1-剖析面试最常见问题之-java-集合框架) - [1.1. 集合概述](#11-集合概述) - [1.1.1. Java 集合概览](#111-java-集合概览) - - [1.1.2. 说说 List,Set,Map 三者的区别?](#112-说说-listsetmap-三者的区别) + - [1.1.2. 说说 List, Set, Queue, Map 四者的区别?](#112-说说-list-set-queue-map-四者的区别) - [1.1.3. 集合框架底层数据结构总结](#113-集合框架底层数据结构总结) - [1.1.3.1. List](#1131-list) - [1.1.3.2. Set](#1132-set) - - [1.1.3.3. Map](#1133-map) + - [1.1.3.3 Queue](#1133-queue) + - [1.1.3.4. Map](#1134-map) - [1.1.4. 如何选用集合?](#114-如何选用集合) - [1.1.5. 为什么要使用集合?](#115-为什么要使用集合) - [1.2. Collection 子接口之 List](#12-collection-子接口之-list) @@ -22,25 +23,29 @@ - [1.3.1.2. 重写 compareTo 方法实现按年龄来排序](#1312-重写-compareto-方法实现按年龄来排序) - [1.3.2. 无序性和不可重复性的含义是什么](#132-无序性和不可重复性的含义是什么) - [1.3.3. 比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同](#133-比较-hashsetlinkedhashset-和-treeset-三者的异同) - - [1.4. Map 接口](#14-map-接口) - - [1.4.1. HashMap 和 Hashtable 的区别](#141-hashmap-和-hashtable-的区别) - - [1.4.2. HashMap 和 HashSet 区别](#142-hashmap-和-hashset-区别) - - [1.4.3. HashMap 和 TreeMap 区别](#143-hashmap-和-treemap-区别) - - [1.4.4. HashSet 如何检查重复](#144-hashset-如何检查重复) - - [1.4.5. HashMap 的底层实现](#145-hashmap-的底层实现) - - [1.4.5.1. JDK1.8 之前](#1451-jdk18-之前) - - [1.4.5.2. JDK1.8 之后](#1452-jdk18-之后) - - [1.4.6. HashMap 的长度为什么是 2 的幂次方](#146-hashmap-的长度为什么是-2-的幂次方) - - [1.4.7. HashMap 多线程操作导致死循环问题](#147-hashmap-多线程操作导致死循环问题) - - [1.4.8. HashMap 有哪几种常见的遍历方式?](#148-hashmap-有哪几种常见的遍历方式) - - [1.4.9. ConcurrentHashMap 和 Hashtable 的区别](#149-concurrenthashmap-和-hashtable-的区别) - - [1.4.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现](#1410-concurrenthashmap-线程安全的具体实现方式底层具体实现) - - [1.4.10.1. JDK1.7(上面有示意图)](#14101-jdk17上面有示意图) - - [1.4.10.2. JDK1.8 (上面有示意图)](#14102-jdk18-上面有示意图) - - [1.5. Collections 工具类](#15-collections-工具类) - - [1.5.1. 排序操作](#151-排序操作) - - [1.5.2. 查找,替换操作](#152-查找替换操作) - - [1.5.3. 同步控制](#153-同步控制) + - [1.4 Collection 子接口之 Queue](#14-collection-子接口之-queue) + - [1.4.1 Queue 与 Deque 的区别](#141-queue-与-deque-的区别) + - [1.4.2 ArrayDeque 与 LinkedList 的区别](#142-arraydeque-与-linkedlist-的区别) + - [1.4.3 说一说 PriorityQueue](#143-说一说-priorityqueue) + - [1.5. Map 接口](#15-map-接口) + - [1.5.1. HashMap 和 Hashtable 的区别](#151-hashmap-和-hashtable-的区别) + - [1.5.2. HashMap 和 HashSet 区别](#152-hashmap-和-hashset-区别) + - [1.5.3. HashMap 和 TreeMap 区别](#153-hashmap-和-treemap-区别) + - [1.5.4. HashSet 如何检查重复](#154-hashset-如何检查重复) + - [1.5.5. HashMap 的底层实现](#155-hashmap-的底层实现) + - [1.5.5.1. JDK1.8 之前](#1551-jdk18-之前) + - [1.5.5.2. JDK1.8 之后](#1552-jdk18-之后) + - [1.5.6. HashMap 的长度为什么是 2 的幂次方](#156-hashmap-的长度为什么是-2-的幂次方) + - [1.5.7. HashMap 多线程操作导致死循环问题](#157-hashmap-多线程操作导致死循环问题) + - [1.5.8. HashMap 有哪几种常见的遍历方式?](#158-hashmap-有哪几种常见的遍历方式) + - [1.5.9. ConcurrentHashMap 和 Hashtable 的区别](#159-concurrenthashmap-和-hashtable-的区别) + - [1.5.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现](#1510-concurrenthashmap-线程安全的具体实现方式底层具体实现) + - [1.5.10.1. JDK1.7(上面有示意图)](#15101-jdk17上面有示意图) + - [1.5.10.2. JDK1.8 (上面有示意图)](#15102-jdk18-上面有示意图) + - [1.6. Collections 工具类](#16-collections-工具类) + - [1.6.1. 排序操作](#161-排序操作) + - [1.6.2. 查找,替换操作](#162-查找替换操作) + - [1.6.3. 同步控制](#163-同步控制) @@ -50,19 +55,21 @@ ### 1.1.1. Java 集合概览 -从下图可以看出,在 Java 中除了以 `Map` 结尾的类之外, 其他类都实现了 `Collection` 接口。 +Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 `Collecton`接口,主要用于存放单一元素;另一个是 `Map` 接口,主要用于存放键值对。对于`Collection` 接口,下面又有三个主要的子接口:`List`、`Set` 和 `Queue`。 -并且,以 `Map` 结尾的类都实现了 `Map` 接口。 +Java 集合框架如下图所示: -![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/source-code/dubbo/java-collection-hierarchy.png) +![](./images/java-collection-hierarchy.png) -

https://www.javatpoint.com/collections-in-java

-### 1.1.2. 说说 List,Set,Map 三者的区别? +注:图中只列举了主要的继承派生关系,并没有列举所有关系。比方省略了`AbstractList`, `NavigableSet`等抽象类以及其他的一些辅助类,如想深入了解,可自行查看源码。 -- `List`(对付顺序的好帮手): 存储的元素是有序的、可重复的。 +### 1.1.2. 说说 List, Set, Queue, Map 四者的区别? + +- `List`(对付顺序的好帮手): 存储的元素是有序的、可重复的。 - `Set`(注重独一无二的性质): 存储的元素是无序的、不可重复的。 -- `Map`(用 Key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x”代表 key,"y"代表 value,Key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。 +- `Queue`(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。 +- `Map`(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),"x" 代表 key,"y" 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。 ### 1.1.3. 集合框架底层数据结构总结 @@ -70,19 +77,23 @@ #### 1.1.3.1. List -- `Arraylist`: `Object[]`数组 -- `Vector`:`Object[]`数组 +- `Arraylist`: `Object[]` 数组 +- `Vector`:`Object[]` 数组 - `LinkedList`: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环) #### 1.1.3.2. Set -- `HashSet`(无序,唯一): 基于 `HashMap` 实现的,底层采用 `HashMap` 来保存元素 -- `LinkedHashSet`:`LinkedHashSet` 是 `HashSet` 的子类,并且其内部是通过 `LinkedHashMap` 来实现的。有点类似于我们之前说的 `LinkedHashMap` 其内部是基于 `HashMap` 实现一样,不过还是有一点点区别的 -- `TreeSet`(有序,唯一): 红黑树(自平衡的排序二叉树) +- `HashSet`(无序,唯一): 基于 `HashMap` 实现的,底层采用 `HashMap` 来保存元素 +- `LinkedHashSet`: `LinkedHashSet` 是 `HashSet` 的子类,并且其内部是通过 `LinkedHashMap` 来实现的。有点类似于我们之前说的 `LinkedHashMap` 其内部是基于 `HashMap` 实现一样,不过还是有一点点区别的 +- `TreeSet`(有序,唯一): 红黑树(自平衡的排序二叉树) + +#### 1.1.3.3 Queue +- `PriorityQueue`: `Object[]` 数组来实现二叉堆 +- `ArrayQueue`: `Object[]` 数组 + 双指针 再来看看 `Map` 接口下面的集合。 -#### 1.1.3.3. Map +#### 1.1.3.4. Map - `HashMap`: JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间 - `LinkedHashMap`: `LinkedHashMap` 继承自 `HashMap`,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,`LinkedHashMap` 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931) @@ -304,9 +315,66 @@ Output: `TreeSet` 底层使用红黑树,能够按照添加元素的顺序进行遍历,排序的方式有自然排序和定制排序。 -## 1.4. Map 接口 +## 1.4 Collection 子接口之 Queue + +### 1.4.1 Queue 与 Deque 的区别 + +`Queue` 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 **先进先出(FIFO)** 规则。 + +`Queue` 扩展了 `Collection` 的接口,根据 **因为容量问题而导致操作失败后处理方式的不同** 可以分为两类方法: 一种在操作失败后会抛出异常,另一种则会返回特殊值。 + +| `Queue` 接口| 抛出异常 | 返回特殊值 | +| ------------ | --------- | ---------- | +| 插入队尾 | add(E e) | offer(E e) | +| 删除队首 | remove() | poll() | +| 查询队首元素 | element() | peek() | + +`Deque` 是双端队列,在队列的两端均可以插入或删除元素。 + +`Deque` 扩展了 `Queue` 的接口, 增加了在队首和队尾进行插入和删除的方法,同样根据失败后处理方式的不同分为两类: + +| `Deque` 接口 | 抛出异常 | 返回特殊值 | +| ------------ | ------------- | --------------- | +| 插入队首 | addFirst(E e) | offerFirst(E e) | +| 插入队尾 | addLast(E e) | offerLast(E e) | +| 删除队首 | removeFirst() | pollFirst() | +| 删除队尾 | removeLast() | pollLast() | +| 查询队首元素 | getFirst() | peekFirst() | +| 查询队尾元素 | getLast() | peekLast() | + +事实上,`Deque` 还提供有 `push()` 和 `pop()` 等其他方法,可用于模拟栈。 + + +### 1.4.2 ArrayDeque 与 LinkedList 的区别 + +`ArrayDeque` 和 `LinkedList` 都实现了 `Deque` 接口,两者都具有队列的功能,但两者有什么区别呢? + +- `ArrayDeque` 是基于可变长的数组和双指针来实现,而 `LinkedList` 则通过链表来实现。 + +- `ArrayDeque` 不支持存储 `NULL` 数据,但 `LinkedList` 支持。 + +- `ArrayDeque` 是在 JDK1.6 才被引入的,而`LinkedList` 早在 JDK1.2 时就已经存在。 + +- `ArrayDeque` 插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 `LinkedList` 不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。 + +从性能的角度上,选用 `ArrayDeque` 来实现队列要比 `LinkedList` 更好。此外,`ArrayDeque` 也可以用于实现栈。 + +### 1.4.3 说一说 PriorityQueue + +`PriorityQueue` 是在 JDK1.5 中被引入的, 其与 `Queue` 的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。 + +这里列举其相关的一些要点: + +- `PriorityQueue` 利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据 +- `PriorityQueue` 通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。 +- `PriorityQueue` 是非线程安全的,且不支持存储 `NULL` 和 `non-comparable` 的对象。 +- `PriorityQueue` 默认是小顶堆,但可以接收一个 `Comparator` 作为构造参数,从而来自定义元素优先级的先后。 + +`PriorityQueue` 在面试中可能更多的会出现在手撕算法的时候,典型例题包括堆排序、求第K大的数、带权图的遍历等,所以需要会熟练使用才行。 + +## 1.5. Map 接口 -### 1.4.1. HashMap 和 Hashtable 的区别 +### 1.5.1. HashMap 和 Hashtable 的区别 1. **线程是否安全:** `HashMap` 是非线程安全的,`HashTable` 是线程安全的,因为 `HashTable` 内部的方法基本都经过`synchronized` 修饰。(如果你要保证线程安全的话就使用 `ConcurrentHashMap` 吧!); 2. **效率:** 因为线程安全的问题,`HashMap` 要比 `HashTable` 效率高一点。另外,`HashTable` 基本被淘汰,不要在代码中使用它; @@ -351,7 +419,7 @@ Output: } ``` -### 1.4.2. HashMap 和 HashSet 区别 +### 1.5.2. HashMap 和 HashSet 区别 如果你看过 `HashSet` 源码的话就应该知道:`HashSet` 底层就是基于 `HashMap` 实现的。(`HashSet` 的源码非常非常少,因为除了 `clone()`、`writeObject()`、`readObject()`是 `HashSet` 自己不得不实现之外,其他方法都是直接调用 `HashMap` 中的方法。 @@ -362,7 +430,7 @@ Output: | 调用 `put()`向 map 中添加元素 | 调用 `add()`方法向 `Set` 中添加元素 | | `HashMap` 使用键(Key)计算 `hashcode` | `HashSet` 使用成员对象来计算 `hashcode` 值,对于两个对象来说 `hashcode` 可能相同,所以`equals()`方法用来判断对象的相等性 | -### 1.4.3. HashMap 和 TreeMap 区别 +### 1.5.3. HashMap 和 TreeMap 区别 `TreeMap` 和`HashMap` 都继承自`AbstractMap` ,但是需要注意的是`TreeMap`它还实现了`NavigableMap`接口和`SortedMap` 接口。 @@ -430,7 +498,7 @@ TreeMap treeMap = new TreeMap<>((person1, person2) -> { **综上,相比于`HashMap`来说 `TreeMap` 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。** -### 1.4.4. HashSet 如何检查重复 +### 1.5.4. HashSet 如何检查重复 以下内容摘自我的 Java 启蒙书《Head first java》第二版: @@ -473,9 +541,9 @@ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 对于引用类型(包括包装类型)来说,equals 如果没有被重写,对比它们的地址是否相等;如果 equals()方法被重写(例如 String),则比较的是地址里的内容。 -### 1.4.5. HashMap 的底层实现 +### 1.5.5. HashMap 的底层实现 -#### 1.4.5.1. JDK1.8 之前 +#### 1.5.5.1. JDK1.8 之前 JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。** @@ -514,7 +582,7 @@ static int hash(int h) { ![jdk1.8之前的内部结构-HashMap](images/jdk1.8之前的内部结构-HashMap.png) -#### 1.4.5.2. JDK1.8 之后 +#### 1.5.5.2. JDK1.8 之后 相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。 @@ -522,7 +590,7 @@ static int hash(int h) { > TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。 -### 1.4.6. HashMap 的长度为什么是 2 的幂次方 +### 1.5.6. HashMap 的长度为什么是 2 的幂次方 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个 40 亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n 代表数组长度)。这也就解释了 HashMap 的长度为什么是 2 的幂次方。 @@ -530,17 +598,17 @@ static int hash(int h) { 我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。** -### 1.4.7. HashMap 多线程操作导致死循环问题 +### 1.5.7. HashMap 多线程操作导致死循环问题 主要原因在于并发下的 Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。 详情请查看: -### 1.4.8. HashMap 有哪几种常见的遍历方式? +### 1.5.8. HashMap 有哪几种常见的遍历方式? [HashMap 的 7 种遍历方式与性能分析!](https://mp.weixin.qq.com/s/zQBN3UvJDhRTKP6SzcZFKw) -### 1.4.9. ConcurrentHashMap 和 Hashtable 的区别 +### 1.5.9. ConcurrentHashMap 和 Hashtable 的区别 `ConcurrentHashMap` 和 `Hashtable` 的区别主要体现在实现线程安全的方式上不同。 @@ -567,9 +635,9 @@ static int hash(int h) { JDK1.8 的 `ConcurrentHashMap` 不在是 **Segment 数组 + HashEntry 数组 + 链表**,而是 **Node 数组 + 链表 / 红黑树**。不过,Node 只能用于链表的情况,红黑树的情况需要使用 **`TreeNode`**。当冲突链表达到一定长度时,链表会转换成红黑树。 -### 1.4.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 +### 1.5.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 -#### 1.4.10.1. JDK1.7(上面有示意图) +#### 1.5.10.1. JDK1.7(上面有示意图) 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 @@ -584,13 +652,13 @@ static class Segment extends ReentrantLock implements Serializable { 一个 `ConcurrentHashMap` 里包含一个 `Segment` 数组。`Segment` 的结构和 `HashMap` 类似,是一种数组和链表结构,一个 `Segment` 包含一个 `HashEntry` 数组,每个 `HashEntry` 是一个链表结构的元素,每个 `Segment` 守护着一个 `HashEntry` 数组里的元素,当对 `HashEntry` 数组的数据进行修改时,必须首先获得对应的 `Segment` 的锁。 -#### 1.4.10.2. JDK1.8 (上面有示意图) +#### 1.5.10.2. JDK1.8 (上面有示意图) `ConcurrentHashMap` 取消了 `Segment` 分段锁,采用 CAS 和 `synchronized` 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N))) `synchronized` 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。 -## 1.5. Collections 工具类 +## 1.6. Collections 工具类 Collections 工具类常用方法: @@ -598,7 +666,7 @@ Collections 工具类常用方法: 2. 查找,替换操作 3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合) -### 1.5.1. 排序操作 +### 1.6.1. 排序操作 ```java void reverse(List list)//反转 @@ -609,7 +677,7 @@ void swap(List list, int i , int j)//交换两个索引位置的元素 void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面 ``` -### 1.5.2. 查找,替换操作 +### 1.6.2. 查找,替换操作 ```java int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的 @@ -621,7 +689,7 @@ int indexOfSubList(List list, List target)//统计target在list中第一次出 boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素 ``` -### 1.5.3. 同步控制 +### 1.6.3. 同步控制 `Collections` 提供了多个`synchronizedXxx()`方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。 diff --git a/docs/java/collection/images/java-collection-hierarchy.png b/docs/java/collection/images/java-collection-hierarchy.png new file mode 100644 index 00000000000..78daf980845 Binary files /dev/null and b/docs/java/collection/images/java-collection-hierarchy.png differ diff --git "a/docs/java/jvm/Java\345\206\205\345\255\230\345\214\272\345\237\237.md" "b/docs/java/jvm/Java\345\206\205\345\255\230\345\214\272\345\237\237.md" index 11210af18d8..918585a0d6e 100644 --- "a/docs/java/jvm/Java\345\206\205\345\255\230\345\214\272\345\237\237.md" +++ "b/docs/java/jvm/Java\345\206\205\345\255\230\345\214\272\345\237\237.md" @@ -371,7 +371,7 @@ System.out.println(str3 == str5);//true System.out.println(str4 == str5);//false ``` -![字符串拼接](./pictures/java内存区域/字符串拼接-常量池2.png) +![字符串拼接](./pictures/java内存区域/字符串拼接-常量池.png) 尽量避免多个字符串拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。 diff --git "a/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.drawio" "b/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.drawio" new file mode 100644 index 00000000000..764cc0be828 --- /dev/null +++ "b/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.drawio" @@ -0,0 +1 @@ +7Vpdb6M4FP011j61AoMNPCZpZkYrrbRStZqZRxccsOLgDDhNMr9+bTAhfKSTtqG0melL8L3mYp9zbny5DXBmq93njKyTf0REOYBWtAPOHYDQti2sPrRlX1r8AJaGOGORmVQb7tlPaoyWsW5YRPPGRCkEl2zdNIYiTWkoGzaSZWLbnLYQvPnUNYlpx3AfEt61fmWRTMwuoFfbv1AWJ9WTbRyUnhWpJpud5AmJxPbI5MyBM8uEkOXVajejXINX4VLe9+mE97CwjKbynBuW6c/Zcu/fPOZp8vjfarvJ/57cmCiPhG/MhgHEXMWbLoQKq1Yt9wYK/GMjKsdNXhA1UROgs97VTnUV689cZnYVSS2pDFa6DBqHuDATmzSiepW2cm8TJun9moTau1WiUrZErrhxx5zkubk+IFoMllSGiRmYXdFM0t1JuOwDCUq9VKyozPZqirnBN7QZ3TqeGW9rFdiWsSVHCkDGRozw4kPkmht1Yeh5BlVwOKrQVVGF0dhUOcNR5V4VVcgfmyp3OKqc66LKHpsqNBxV8Kqoct2xqcI9VH0IJCFCTSitLpSOi7pQOtZQWHonZZ+vSfpy2dcWlQANd5UJZfwPlQmHMq3izxk7FfzB6WNpfLX0jV4fBAMdOo3sO8XgBzyK2gyi0ROwqlueOoxUGLbO6a9xFMrDpN6bOmMV1YzzmeAiK8I4EaH+Iiw5FUt65MGhTx8Wg59ePr6FrQOsp2yD2LvtOcKgjYaioKfNMEdgYgEfd7hQ25dN0JtopiKlLeiNiXAWp2oYKriosk81mCwkfGIcKxZF/BTLdT5ZPUSrTDTNKTu4DFlu0HptrcqKI6bcnkSBgyVKT4dBs3QHAg/MPTCZgQkGcxdMfTAtXPrCB/MA+B7wP4E5BlNbs/q7UuqgTv6hoMuqDfvzbzBi+/oR7W/ANJrodqnGWp8VLGwS1kaL7pj8Znz6+rsGWe2qHN3tDObFYG8G5UNp1Om4tvBVCxObLKRPbamcJ0kWU/mr4rnL1xEZ6ImzKKOcSPbYXG4fPeYJ/wpW1ASVfk69l1Uhym2au2qSO4Gg0wzkBK1AJQ6dQIVaDtt+hYD6uiSDCMh7MwWhMxXkj6qgdjVlv1RBsCVF/MYK6mvevFJBFxSDe6YYgnclBv9CYjjU6G8lhm576K8zaoaMqpcq8lBM0F8Ga72+YsVoCtDds6oFTh4on5JwGRc1wlGlvij+nlMfmH89mpWBA7rHQnwiKU5WE9ati3CDqOrYe6mQqiliscjpMNSe7lb9eV/uLRmx26AYj967tfs6Vh+7WnTO/Hq331W5iC9VLqK3Lhf7mmavVVCq1vSt0okeHGlID2sRFaPnq+idH/5tcXjoMuLAGF1IHGpY/5qlnF7/JsiZ/w8= \ No newline at end of file diff --git "a/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.png" "b/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.png" new file mode 100644 index 00000000000..4c6f20e9508 Binary files /dev/null and "b/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\240.png" differ diff --git "a/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\2402.png" "b/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\2402.png" deleted file mode 100644 index b680fb60204..00000000000 Binary files "a/docs/java/jvm/pictures/java\345\206\205\345\255\230\345\214\272\345\237\237/\345\255\227\347\254\246\344\270\262\346\213\274\346\216\245-\345\270\270\351\207\217\346\261\2402.png" and /dev/null differ diff --git "a/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" index 8b92632dc57..365d5aec088 100644 --- "a/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" @@ -619,8 +619,7 @@ public ThreadPoolExecutor(int corePoolSize, 如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,`ThreadPoolTaskExecutor` 定义一些策略: - **`ThreadPoolExecutor.AbortPolicy`:** 抛出 `RejectedExecutionException`来拒绝新任务的处理。 -- **`ThreadPoolExecutor.CallerRunsPolicy`:** -调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 +- **`ThreadPoolExecutor.CallerRunsPolicy`:** 调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 - **`ThreadPoolExecutor.DiscardPolicy`:** 不处理新任务,直接丢弃掉。 - **`ThreadPoolExecutor.DiscardOldestPolicy`:** 此策略将丢弃最早的未处理的任务请求。 diff --git "a/docs/java/multi-thread/java\347\272\277\347\250\213\346\261\240\345\255\246\344\271\240\346\200\273\347\273\223.md" "b/docs/java/multi-thread/java\347\272\277\347\250\213\346\261\240\345\255\246\344\271\240\346\200\273\347\273\223.md" index eb266f3aac2..1adf4081fef 100644 --- "a/docs/java/multi-thread/java\347\272\277\347\250\213\346\261\240\345\255\246\344\271\240\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/java\347\272\277\347\250\213\346\261\240\345\255\246\344\271\240\346\200\273\347\273\223.md" @@ -173,10 +173,10 @@ public class ScheduledThreadPoolExecutor 如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,`ThreadPoolTaskExecutor` 定义一些策略: -- **`ThreadPoolExecutor.AbortPolicy`**:抛出 `RejectedExecutionException`来拒绝新任务的处理。 -- **`ThreadPoolExecutor.CallerRunsPolicy`**:调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 -- **`ThreadPoolExecutor.DiscardPolicy`:** 不处理新任务,直接丢弃掉。 -- **`ThreadPoolExecutor.DiscardOldestPolicy`:** 此策略将丢弃最早的未处理的任务请求。 +- **`ThreadPoolExecutor.AbortPolicy`** :抛出 `RejectedExecutionException`来拒绝新任务的处理。 +- **`ThreadPoolExecutor.CallerRunsPolicy`** :调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 +- **`ThreadPoolExecutor.DiscardPolicy`** :不处理新任务,直接丢弃掉。 +- **`ThreadPoolExecutor.DiscardOldestPolicy`** : 此策略将丢弃最早的未处理的任务请求。 举个例子: diff --git "a/docs/java/multi-thread/\344\270\207\345\255\227\350\257\246\350\247\243ThreadLocal\345\205\263\351\224\256\345\255\227.md" "b/docs/java/multi-thread/\344\270\207\345\255\227\350\257\246\350\247\243ThreadLocal\345\205\263\351\224\256\345\255\227.md" index 3650ddbb25e..a7c79807775 100644 --- "a/docs/java/multi-thread/\344\270\207\345\255\227\350\257\246\350\247\243ThreadLocal\345\205\263\351\224\256\345\255\227.md" +++ "b/docs/java/multi-thread/\344\270\207\345\255\227\350\257\246\350\247\243ThreadLocal\345\205\263\351\224\256\345\255\227.md" @@ -1,19 +1,19 @@ -> 本文来自一枝花算不算浪漫投稿, 原文地址:[https://juejin.im/post/5eacc1c75188256d976df748](https://juejin.im/post/5eacc1c75188256d976df748)。 +> 本文来自一枝花算不算浪漫投稿, 原文地址:[https://juejin.im/post/5eacc1c75188256d976df748](https://juejin.im/post/5eacc1c75188256d976df748)。 ### 前言 ![](./images/thread-local/1.png) -**全文共10000+字,31张图,这篇文章同样耗费了不少的时间和精力才创作完成,原创不易,请大家点点关注+在看,感谢。** +**全文共 10000+字,31 张图,这篇文章同样耗费了不少的时间和精力才创作完成,原创不易,请大家点点关注+在看,感谢。** 对于`ThreadLocal`,大家的第一反应可能是很简单呀,线程的变量副本,每个线程隔离。那这里有几个问题大家可以思考一下: -- `ThreadLocal`的key是**弱引用**,那么在 `ThreadLocal`.get()的时候,发生**GC**之后,key是否为**null**? +- `ThreadLocal`的 key 是**弱引用**,那么在 `ThreadLocal`.get()的时候,发生**GC**之后,key 是否为**null**? - `ThreadLocal`中`ThreadLocalMap`的**数据结构**? -- `ThreadLocalMap`的**Hash算法**? -- `ThreadLocalMap`中**Hash冲突**如何解决? +- `ThreadLocalMap`的**Hash 算法**? +- `ThreadLocalMap`中**Hash 冲突**如何解决? - `ThreadLocalMap`的**扩容机制**? -- `ThreadLocalMap`中**过期key的清理机制**?**探测式清理**和**启发式清理**流程? +- `ThreadLocalMap`中**过期 key 的清理机制**?**探测式清理**和**启发式清理**流程? - `ThreadLocalMap.set()`方法实现原理? - `ThreadLocalMap.get()`方法实现原理? - 项目中`ThreadLocal`使用情况?遇到的坑? @@ -23,8 +23,6 @@ ### 目录 - - **注明:** 本文源码基于`JDK 1.8` ### `ThreadLocal`代码演示 @@ -80,19 +78,18 @@ size: 0 我们还要注意`Entry`, 它的`key`是`ThreadLocal k` ,继承自`WeakReference`, 也就是我们常说的弱引用类型。 -### GC 之后key是否为null? +### GC 之后 key 是否为 null? 回应开头的那个问题, `ThreadLocal` 的`key`是弱引用,那么在`ThreadLocal.get()`的时候,发生`GC`之后,`key`是否是`null`? 为了搞清楚这个问题,我们需要搞清楚`Java`的**四种引用类型**: -- **强引用**:我们常常new出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候 -- **软引用**:使用SoftReference修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收 -- **弱引用**:使用WeakReference修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收 +- **强引用**:我们常常 new 出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候 +- **软引用**:使用 SoftReference 修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收 +- **弱引用**:使用 WeakReference 修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收 - **虚引用**:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知 - -接着再来看下代码,我们使用反射的方式来看看`GC`后`ThreadLocal`中的数据情况:(下面代码来源自:https://blog.csdn.net/thewindkee/article/details/103726942 本地运行演示GC回收场景) +接着再来看下代码,我们使用反射的方式来看看`GC`后`ThreadLocal`中的数据情况:(下面代码来源自:https://blog.csdn.net/thewindkee/article/details/103726942 本地运行演示 GC 回收场景) ```java public class ThreadLocalDemo { @@ -140,6 +137,7 @@ public class ThreadLocalDemo { ``` 结果如下: + ```java 弱引用key:java.lang.ThreadLocal@433619b6,值:abc 弱引用key:java.lang.ThreadLocal@418a15e3,值:java.lang.ref.SoftReference@bf97a12 @@ -192,7 +190,7 @@ void createMap(Thread t, T firstValue) { 主要的核心逻辑还是在`ThreadLocalMap`中的,一步步往下看,后面还有更详细的剖析。 -### `ThreadLocalMap` Hash算法 +### `ThreadLocalMap` Hash 算法 既然是`Map`结构,那么`ThreadLocalMap`当然也要实现自己的`hash`算法来解决散列表数组冲突问题。 @@ -200,7 +198,7 @@ void createMap(Thread t, T firstValue) { int i = key.threadLocalHashCode & (len-1); ``` -`ThreadLocalMap`中`hash`算法很简单,这里`i`就是当前key在散列表中对应的数组下标位置。 +`ThreadLocalMap`中`hash`算法很简单,这里`i`就是当前 key 在散列表中对应的数组下标位置。 这里最关键的就是`threadLocalHashCode`值的计算,`ThreadLocal`中有一个属性为`HASH_INCREMENT = 0x61c88647` @@ -215,7 +213,7 @@ public class ThreadLocal { private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } - + static class ThreadLocalMap { ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; @@ -231,7 +229,7 @@ public class ThreadLocal { 每当创建一个`ThreadLocal`对象,这个`ThreadLocal.nextHashCode` 这个值就会增长 `0x61c88647` 。 -这个值很特殊,它是**斐波那契数** 也叫 **黄金分割数**。`hash`增量为 这个数字,带来的好处就是 `hash` **分布非常均匀**。 +这个值很特殊,它是**斐波那契数** 也叫 **黄金分割数**。`hash`增量为 这个数字,带来的好处就是 `hash` **分布非常均匀**。 我们自己可以尝试下: @@ -239,7 +237,7 @@ public class ThreadLocal { 可以看到产生的哈希码分布很均匀,这里不去细纠**斐波那契**具体算法,感兴趣的可以自行查阅相关资料。 -### `ThreadLocalMap` Hash冲突 +### `ThreadLocalMap` Hash 冲突 > **注明:** 下面所有示例图中,**绿色块**`Entry`代表**正常数据**,**灰色块**代表`Entry`的`key`值为`null`,**已被垃圾回收**。**白色块**表示`Entry`为`null`。 @@ -247,22 +245,21 @@ public class ThreadLocal { `HashMap`中解决冲突的方法是在数组上构造一个**链表**结构,冲突的数据挂载到链表上,如果链表长度超过一定数量则会转化成**红黑树**。 -而`ThreadLocalMap`中并没有链表结构,所以这里不能适用`HashMap`解决冲突的方式了。 +而 `ThreadLocalMap` 中并没有链表结构,所以这里不能使用 `HashMap` 解决冲突的方式了。 ![](./images/thread-local/7.png) +如上图所示,如果我们插入一个`value=27`的数据,通过 `hash` 计算后应该落入第 4 个槽位中,而槽位 4 已经有了 `Entry` 数据。 -如上图所示,如果我们插入一个`value=27`的数据,通过`hash`计算后应该落入第4个槽位中,而槽位4已经有了`Entry`数据。 - -此时就会线性向后查找,一直找到`Entry`为`null`的槽位才会停止查找,将当前元素放入此槽位中。当然迭代过程中还有其他的情况,比如遇到了`Entry`不为`null`且`key`值相等的情况,还有`Entry`中的`key`值为`null`的情况等等都会有不同的处理,后面会一一详细讲解。 +此时就会线性向后查找,一直找到 `Entry` 为 `null` 的槽位才会停止查找,将当前元素放入此槽位中。当然迭代过程中还有其他的情况,比如遇到了 `Entry` 不为 `null` 且 `key` 值相等的情况,还有 `Entry` 中的 `key` 值为 `null` 的情况等等都会有不同的处理,后面会一一详细讲解。 -这里还画了一个`Entry`中的`key`为`null`的数据(**Entry=2的灰色块数据**),因为`key`值是**弱引用**类型,所以会有这种数据存在。在`set`过程中,如果遇到了`key`过期的`Entry`数据,实际上是会进行一轮**探测式清理**操作的,具体操作方式后面会讲到。 +这里还画了一个`Entry`中的`key`为`null`的数据(**Entry=2 的灰色块数据**),因为`key`值是**弱引用**类型,所以会有这种数据存在。在`set`过程中,如果遇到了`key`过期的`Entry`数据,实际上是会进行一轮**探测式清理**操作的,具体操作方式后面会讲到。 ### `ThreadLocalMap.set()`详解 #### `ThreadLocalMap.set()`原理图解 -看完了`ThreadLocal` **hash算法**后,我们再来看`set`是如何实现的。 +看完了`ThreadLocal` **hash 算法**后,我们再来看`set`是如何实现的。 往`ThreadLocalMap`中`set`数据(**新增**或者**更新**数据)分为好几种情况,针对不同的情况我们画图来说说明。 @@ -282,36 +279,33 @@ public class ThreadLocal { ![](./images/thread-local/11.png) -遍历散列数组,线性往后查找,如果找到`Entry`为`null`的槽位,则将数据放入该槽位中,或者往后遍历过程中,遇到了**key值相等**的数据,直接更新即可。 +遍历散列数组,线性往后查找,如果找到`Entry`为`null`的槽位,则将数据放入该槽位中,或者往后遍历过程中,遇到了**key 值相等**的数据,直接更新即可。 **第四种情况:** 槽位数据不为空,往后遍历过程中,在找到`Entry`为`null`的槽位之前,遇到`key`过期的`Entry`,如下图,往后遍历过程中,一到了`index=7`的槽位数据`Entry`的`key=null`: ![](./images/thread-local/12.png) -散列数组下标为7位置对应的`Entry`数据`key`为`null`,表明此数据`key`值已经被垃圾回收掉了,此时就会执行`replaceStaleEntry()`方法,该方法含义是**替换过期数据的逻辑**,以**index=7**位起点开始遍历,进行探测式数据清理工作。 +散列数组下标为 7 位置对应的`Entry`数据`key`为`null`,表明此数据`key`值已经被垃圾回收掉了,此时就会执行`replaceStaleEntry()`方法,该方法含义是**替换过期数据的逻辑**,以**index=7**位起点开始遍历,进行探测式数据清理工作。 初始化探测式清理过期数据扫描的开始位置:`slotToExpunge = staleSlot = 7` 以当前`staleSlot`开始 向前迭代查找,找其他过期的数据,然后更新过期数据起始扫描下标`slotToExpunge`。`for`循环迭代,直到碰到`Entry`为`null`结束。 -如果找到了过期的数据,继续向前迭代,直到遇到`Entry=null`的槽位才停止迭代,如下图所示,**slotToExpunge被更新为0**: +如果找到了过期的数据,继续向前迭代,直到遇到`Entry=null`的槽位才停止迭代,如下图所示,**slotToExpunge 被更新为 0**: ![](./images/thread-local/13.png) -以当前节点(`index=7`)向前迭代,检测是否有过期的`Entry`数据,如果有则更新`slotToExpunge`值。碰到`null`则结束探测。以上图为例`slotToExpunge`被更新为0。 +以当前节点(`index=7`)向前迭代,检测是否有过期的`Entry`数据,如果有则更新`slotToExpunge`值。碰到`null`则结束探测。以上图为例`slotToExpunge`被更新为 0。 上面向前迭代的操作是为了更新探测清理过期数据的起始下标`slotToExpunge`的值,这个值在后面会讲解,它是用来判断当前过期槽位`staleSlot`之前是否还有过期元素。 -接着开始以`staleSlot`位置(index=7)向后迭代,**如果找到了相同key值的Entry数据:** +接着开始以`staleSlot`位置(index=7)向后迭代,**如果找到了相同 key 值的 Entry 数据:** ![](./images/thread-local/14.png) 从当前节点`staleSlot`向后查找`key`值相等的`Entry`元素,找到后更新`Entry`的值并交换`staleSlot`元素的位置(`staleSlot`位置为过期元素),更新`Entry`数据,然后开始进行过期`Entry`的清理工作,如下图所示: -![Yu4oWT.png](https://user-gold-cdn.xitu.io/2020/5/8/171f3ba9af057e1e?w=1336&h=361&f=png&s=63049) - - -**向后遍历过程中,如果没有找到相同key值的Entry数据:** +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/view.png)向后遍历过程中,如果没有找到相同 key 值的 Entry 数据: ![](./images/thread-local/15.png) @@ -367,6 +361,7 @@ int i = key.threadLocalHashCode & (len-1); ``` 什么情况下桶才是可以使用的呢? + 1. `k = key` 说明是替换操作,可以使用 2. 碰到一个过期的桶,执行替换逻辑,占用过期桶 3. 查找过程中,碰到桶中`Entry=null`的情况,直接使用 @@ -386,16 +381,17 @@ private static int prevIndex(int i, int len) { ``` 接着看剩下`for`循环中的逻辑: + 1. 遍历当前`key`值对应的桶中`Entry`数据为空,这说明散列数组这里没有数据冲突,跳出`for`循环,直接`set`数据到对应的桶中 2. 如果`key`值对应的桶中`Entry`数据不为空 -2.1 如果`k = key`,说明当前`set`操作是一个替换操作,做替换逻辑,直接返回 -2.2 如果`key = null`,说明当前桶位置的`Entry`是过期数据,执行`replaceStaleEntry()`方法(核心方法),然后返回 + 2.1 如果`k = key`,说明当前`set`操作是一个替换操作,做替换逻辑,直接返回 + 2.2 如果`key = null`,说明当前桶位置的`Entry`是过期数据,执行`replaceStaleEntry()`方法(核心方法),然后返回 3. `for`循环执行完毕,继续往下执行说明向后迭代的过程中遇到了`entry`为`null`的情况 -3.1 在`Entry`为`null`的桶中创建一个新的`Entry`对象 -3.2 执行`++size`操作 + 3.1 在`Entry`为`null`的桶中创建一个新的`Entry`对象 + 3.2 执行`++size`操作 4. 调用`cleanSomeSlots()`做一次启发式清理工作,清理散列数组中`Entry`的`key`过期的数据 -4.1 如果清理工作完成后,未清理到任何数据,且`size`超过了阈值(数组长度的2/3),进行`rehash()`操作 -4.2 `rehash()`中会先进行一轮探测式清理,清理过期`key`,清理完成后如果**size >= threshold - threshold / 4**,就会执行真正的扩容逻辑(扩容逻辑往后看) + 4.1 如果清理工作完成后,未清理到任何数据,且`size`超过了阈值(数组长度的 2/3),进行`rehash()`操作 + 4.2 `rehash()`中会先进行一轮探测式清理,清理过期`key`,清理完成后如果**size >= threshold - threshold / 4**,就会执行真正的扩容逻辑(扩容逻辑往后看) 接着重点看下`replaceStaleEntry()`方法,`replaceStaleEntry()`方法提供替换过期数据的功能,我们可以对应上面**第四种情况**的原理图来再回顾下,具体代码如下: @@ -446,7 +442,7 @@ private void replaceStaleEntry(ThreadLocal key, Object value, } ``` -`slotToExpunge`表示开始探测式清理过期数据的开始下标,默认从当前的`staleSlot`开始。以当前的`staleSlot`开始,向前迭代查找,找到没有过期的数据,`for`循环一直碰到`Entry`为`null`才会结束。如果向前找到了过期数据,更新探测清理过期数据的开始下标为i,即`slotToExpunge=i` +`slotToExpunge`表示开始探测式清理过期数据的开始下标,默认从当前的`staleSlot`开始。以当前的`staleSlot`开始,向前迭代查找,找到没有过期的数据,`for`循环一直碰到`Entry`为`null`才会结束。如果向前找到了过期数据,更新探测清理过期数据的开始下标为 i,即`slotToExpunge=i` ```java for (int i = prevIndex(staleSlot, len); @@ -460,7 +456,7 @@ for (int i = prevIndex(staleSlot, len); ``` 接着开始从`staleSlot`向后查找,也是碰到`Entry`为`null`的桶结束。 -如果迭代过程中,**碰到k == key**,这说明这里是替换逻辑,替换新数据并且交换当前`staleSlot`位置。如果`slotToExpunge == staleSlot`,这说明`replaceStaleEntry()`一开始向前查找过期数据时并未找到过期的`Entry`数据,接着向后查找过程中也未发现过期数据,修改开始探测式清理过期数据的下标为当前循环的index,即`slotToExpunge = i`。最后调用`cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);`进行启发式过期数据清理。 +如果迭代过程中,**碰到 k == key**,这说明这里是替换逻辑,替换新数据并且交换当前`staleSlot`位置。如果`slotToExpunge == staleSlot`,这说明`replaceStaleEntry()`一开始向前查找过期数据时并未找到过期的`Entry`数据,接着向后查找过程中也未发现过期数据,修改开始探测式清理过期数据的下标为当前循环的 index,即`slotToExpunge = i`。最后调用`cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);`进行启发式过期数据清理。 ```java if (k == key) { @@ -468,7 +464,7 @@ if (k == key) { tab[i] = tab[staleSlot]; tab[staleSlot] = e; - + if (slotToExpunge == staleSlot) slotToExpunge = i; @@ -479,7 +475,7 @@ if (k == key) { `cleanSomeSlots()`和`expungeStaleEntry()`方法后面都会细讲,这两个是和清理相关的方法,一个是过期`key`相关`Entry`的启发式清理(`Heuristically scan`),另一个是过期`key`相关`Entry`的探测式清理。 -**如果k != key**则会接着往下走,`k == null`说明当前遍历的`Entry`是一个过期数据,`slotToExpunge == staleSlot`说明,一开始的向前查找数据并未找到过期的`Entry`。如果条件成立,则更新`slotToExpunge` 为当前位置,这个前提是前驱节点扫描时未发现过期数据。 +**如果 k != key**则会接着往下走,`k == null`说明当前遍历的`Entry`是一个过期数据,`slotToExpunge == staleSlot`说明,一开始的向前查找数据并未找到过期的`Entry`。如果条件成立,则更新`slotToExpunge` 为当前位置,这个前提是前驱节点扫描时未发现过期数据。 ```java if (k == null && slotToExpunge == staleSlot) @@ -494,12 +490,13 @@ tab[staleSlot] = new Entry(key, value); ``` 最后判断除了`staleSlot`以外,还发现了其他过期的`slot`数据,就要开启清理数据的逻辑: + ```java if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); ``` -### `ThreadLocalMap`过期key的探测式清理流程 +### `ThreadLocalMap`过期 key 的探测式清理流程 上面我们有提及`ThreadLocalMap`的两种过期`key`数据清理方式:**探测式清理**和**启发式清理**。 @@ -507,7 +504,7 @@ if (slotToExpunge != staleSlot) ![](./images/thread-local/18.png) -如上图,`set(27)` 经过hash计算后应该落到`index=4`的桶中,由于`index=4`桶已经有了数据,所以往后迭代最终数据放入到`index=7`的桶中,放入后一段时间后`index=5`中的`Entry`数据`key`变为了`null` +如上图,`set(27)` 经过 hash 计算后应该落到`index=4`的桶中,由于`index=4`桶已经有了数据,所以往后迭代最终数据放入到`index=7`的桶中,放入后一段时间后`index=5`中的`Entry`数据`key`变为了`null` ![](./images/thread-local/19.png) @@ -529,7 +526,7 @@ if (slotToExpunge != staleSlot) ![](./images/thread-local/22.png) -执行完第二步后,index=4的元素挪到index=3的槽位中。 +执行完第二步后,index=4 的元素挪到 index=3 的槽位中。 继续往后迭代检查,碰到正常数据,计算该数据位置是否偏移,如果被偏移,则重新计算`slot`位置,目的是让正常数据尽可能存放在正确位置或离正确位置更近的位置 @@ -581,7 +578,7 @@ if (k == null) { e.value = null; tab[i] = null; size--; -} +} ``` 如果`key`没有过期,重新计算当前`key`的下标位置是不是当前槽位下标位置,如果不是,那么说明产生了`hash`冲突,此时以新计算出来正确的槽位位置往后迭代,找到最近一个可以存放`entry`的位置。 @@ -686,7 +683,7 @@ private void resize() { ![](./images/thread-local/27.png) -我们以`get(ThreadLocal1)`为例,通过`hash`计算后,正确的`slot`位置应该是4,而`index=4`的槽位已经有了数据,且`key`值不等于`ThreadLocal1`,所以需要继续往后迭代查找。 +我们以`get(ThreadLocal1)`为例,通过`hash`计算后,正确的`slot`位置应该是 4,而`index=4`的槽位已经有了数据,且`key`值不等于`ThreadLocal1`,所以需要继续往后迭代查找。 迭代到`index=5`的数据时,此时`Entry.key=null`,触发一次探测式数据回收操作,执行`expungeStaleEntry()`方法,执行完后,`index 5,8`的数据都会被回收,而`index 6,7`的数据都会前移,此时继续往后迭代,到`index = 6`的时候即找到了`key`值相等的`Entry`数据,如下图所示: @@ -724,9 +721,7 @@ private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { } ``` - -### `ThreadLocalMap`过期key的启发式清理流程 - +### `ThreadLocalMap`过期 key 的启发式清理流程 上面多次提及到`ThreadLocalMap`过期可以的两种清理方式:**探测式清理(expungeStaleEntry())**、**启发式清理(cleanSomeSlots())** @@ -760,7 +755,7 @@ private boolean cleanSomeSlots(int i, int n) { 我们使用`ThreadLocal`的时候,在异步场景下是无法给子线程共享父线程中创建的线程副本数据的。 -为了解决这个问题,JDK中还有一个`InheritableThreadLocal`类,我们来看一个例子: +为了解决这个问题,JDK 中还有一个`InheritableThreadLocal`类,我们来看一个例子: ```java public class InheritableThreadLocalDemo { @@ -816,11 +811,11 @@ private void init(ThreadGroup g, Runnable target, String name, 我们现在项目中日志记录用的是`ELK+Logstash`,最后在`Kibana`中进行展示和检索。 -现在都是分布式系统统一对外提供服务,项目间调用的关系可以通过traceId来关联,但是不同项目之间如何传递`traceId`呢? +现在都是分布式系统统一对外提供服务,项目间调用的关系可以通过 `traceId` 来关联,但是不同项目之间如何传递 `traceId` 呢? -这里我们使用`org.slf4j.MDC`来实现此功能,内部就是通过`ThreadLocal`来实现的,具体实现如下: +这里我们使用 `org.slf4j.MDC` 来实现此功能,内部就是通过 `ThreadLocal` 来实现的,具体实现如下: -当前端发送请求到**服务A**时,**服务A**会生成一个类似`UUID`的`traceId`字符串,将此字符串放入当前线程的`ThreadLocal`中,在调用**服务B**的时候,将`traceId`写入到请求的`Header`中,**服务B**在接收请求时会先判断请求的`Header`中是否有`traceId`,如果存在则写入自己线程的`ThreadLocal`中。 +当前端发送请求到**服务 A**时,**服务 A**会生成一个类似`UUID`的`traceId`字符串,将此字符串放入当前线程的`ThreadLocal`中,在调用**服务 B**的时候,将`traceId`写入到请求的`Header`中,**服务 B**在接收请求时会先判断请求的`Header`中是否有`traceId`,如果存在则写入自己线程的`ThreadLocal`中。 ![](./images/thread-local/30.png) @@ -830,9 +825,10 @@ private void init(ThreadGroup g, Runnable target, String name, 针对于这些场景,我们都可以有相应的解决方案,如下所示 -#### Feign远程调用解决方案 +#### Feign 远程调用解决方案 **服务发送请求:** + ```java @Component @Slf4j @@ -849,6 +845,7 @@ public class FeignInvokeInterceptor implements RequestInterceptor { ``` **服务接收请求:** + ```java @Slf4j @Component @@ -876,13 +873,13 @@ public class LogInterceptor extends HandlerInterceptorAdapter { } ``` -#### 线程池异步调用,requestId传递 +#### 线程池异步调用,requestId 传递 因为`MDC`是基于`ThreadLocal`去实现的,异步过程中,子线程并没有办法获取到父线程`ThreadLocal`存储的数据,所以这里可以自定义线程池执行器,修改其中的`run()`方法: ```java public class MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { - + @Override public void execute(Runnable runnable) { Map context = MDC.getCopyOfContextMap(); @@ -903,11 +900,6 @@ public class MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { } ``` -#### 使用MQ发送消息给第三方系统 - -在MQ发送的消息体中自定义属性`requestId`,接收方消费消息后,自己解析`requestId`使用即可。 - - - - +#### 使用 MQ 发送消息给第三方系统 +在 MQ 发送的消息体中自定义属性`requestId`,接收方消费消息后,自己解析`requestId`使用即可。 diff --git "a/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" "b/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" index fcc900e6ea6..e4042ebe76b 100644 --- "a/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" @@ -1,50 +1,24 @@ -点击关注[公众号](#公众号 "公众号")及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。 - - - -- [一 JDK 提供的并发容器总结](#一-jdk-提供的并发容器总结 "一 JDK 提供的并发容器总结") -- [二 ConcurrentHashMap](#二-concurrenthashmap "二 ConcurrentHashMap") -- [三 CopyOnWriteArrayList](#三-copyonwritearraylist "三 CopyOnWriteArrayList") - - [3.1 CopyOnWriteArrayList 简介](#31-copyonwritearraylist-简介 "3.1 CopyOnWriteArrayList 简介") - - [3.2 CopyOnWriteArrayList 是如何做到的?](#32-copyonwritearraylist-是如何做到的? "3.2 CopyOnWriteArrayList 是如何做到的?") - - [3.3 CopyOnWriteArrayList 读取和写入源码简单分析](#33-copyonwritearraylist-读取和写入源码简单分析 "3.3 CopyOnWriteArrayList 读取和写入源码简单分析") - - [3.3.1 CopyOnWriteArrayList 读取操作的实现](#331-copyonwritearraylist-读取操作的实现 "3.3.1 CopyOnWriteArrayList 读取操作的实现") - - [3.3.2 CopyOnWriteArrayList 写入操作的实现](#332-copyonwritearraylist-写入操作的实现 "3.3.2 CopyOnWriteArrayList 写入操作的实现") -- [四 ConcurrentLinkedQueue](#四-concurrentlinkedqueue "四 ConcurrentLinkedQueue") -- [五 BlockingQueue](#五-blockingqueue "五 BlockingQueue") - - [5.1 BlockingQueue 简单介绍](#51-blockingqueue-简单介绍 "5.1 BlockingQueue 简单介绍") - - [5.2 ArrayBlockingQueue](#52-arrayblockingqueue "5.2 ArrayBlockingQueue") - - [5.3 LinkedBlockingQueue](#53-linkedblockingqueue "5.3 LinkedBlockingQueue") - - [5.4 PriorityBlockingQueue](#54-priorityblockingqueue "5.4 PriorityBlockingQueue") -- [六 ConcurrentSkipListMap](#六-concurrentskiplistmap "六 ConcurrentSkipListMap") -- [七 参考](#七-参考 "七 参考") - - - -## 一 JDK 提供的并发容器总结 +## JDK 提供的并发容器总结 JDK 提供的这些容器大部分在 `java.util.concurrent` 包中。 -- **ConcurrentHashMap:** 线程安全的 HashMap -- **CopyOnWriteArrayList:** 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector. -- **ConcurrentLinkedQueue:** 高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,这是一个非阻塞队列。 -- **BlockingQueue:** 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。 -- **ConcurrentSkipListMap:** 跳表的实现。这是一个 Map,使用跳表的数据结构进行快速查找。 +- **`ConcurrentHashMap`** : 线程安全的 `HashMap` +- **`CopyOnWriteArrayList`** : 线程安全的 `List`,在读多写少的场合性能非常好,远远好于 `Vector`。 +- **`ConcurrentLinkedQueue`** : 高效的并发队列,使用链表实现。可以看做一个线程安全的 `LinkedList`,这是一个非阻塞队列。 +- **`BlockingQueue`** : 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。 +- **`ConcurrentSkipListMap`** : 跳表的实现。这是一个 Map,使用跳表的数据结构进行快速查找。 -## 二 ConcurrentHashMap +## ConcurrentHashMap -我们知道 HashMap 不是线程安全的,在并发场景下如果要保证一种可行的方式是使用 `Collections.synchronizedMap()` 方法来包装我们的 HashMap。但这是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来不可忽视的性能问题。 +我们知道 `HashMap` 不是线程安全的,在并发场景下如果要保证一种可行的方式是使用 `Collections.synchronizedMap()` 方法来包装我们的 `HashMap`。但这是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来不可忽视的性能问题。 -所以就有了 HashMap 的线程安全版本—— ConcurrentHashMap 的诞生。在 ConcurrentHashMap 中,无论是读操作还是写操作都能保证很高的性能:在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。 +所以就有了 `HashMap` 的线程安全版本—— `ConcurrentHashMap` 的诞生。 -关于 ConcurrentHashMap 相关问题,我在 [Java 集合框架常见面试题](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md "Java集合框架常见面试题") 这篇文章中已经提到过。下面梳理一下关于 ConcurrentHashMap 比较重要的问题: +在 `ConcurrentHashMap` 中,无论是读操作还是写操作都能保证很高的性能:在进行读操作时(几乎)不需要加锁,而在写操作时通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。 -- [ConcurrentHashMap 和 Hashtable 的区别](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md#concurrenthashmap-%E5%92%8C-hashtable-%E7%9A%84%E5%8C%BA%E5%88%AB "ConcurrentHashMap 和 Hashtable 的区别") -- [ConcurrentHashMap 线程安全的具体实现方式/底层具体实现](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/collection/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98.md#concurrenthashmap%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E5%BA%95%E5%B1%82%E5%85%B7%E4%BD%93%E5%AE%9E%E7%8E%B0 "ConcurrentHashMap线程安全的具体实现方式/底层具体实现") +## CopyOnWriteArrayList -## 三 CopyOnWriteArrayList - -### 3.1 CopyOnWriteArrayList 简介 +### CopyOnWriteArrayList 简介 ```java public class CopyOnWriteArrayList @@ -52,21 +26,21 @@ extends Object implements List, RandomAccess, Cloneable, Serializable ``` -在很多应用场景中,读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问 List 的内部数据,毕竟读取操作是安全的。 +在很多应用场景中,读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问 `List` 的内部数据,毕竟读取操作是安全的。 这和我们之前在多线程章节讲过 `ReentrantReadWriteLock` 读写锁的思想非常类似,也就是读读共享、写写互斥、读写互斥、写读互斥。JDK 中提供了 `CopyOnWriteArrayList` 类比相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致,`CopyOnWriteArrayList` 读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。这样一来,读操作的性能就会大幅度提升。**那它是怎么做的呢?** -### 3.2 CopyOnWriteArrayList 是如何做到的? +### CopyOnWriteArrayList 是如何做到的? `CopyOnWriteArrayList` 类的所有可变操作(add,set 等等)都是通过创建底层数组的新副本来实现的。当 List 需要被修改的时候,我并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。写完之后,再将修改完的副本替换原来的数据,这样就可以保证写操作不会影响读操作了。 -从 `CopyOnWriteArrayList` 的名字就能看出`CopyOnWriteArrayList` 是满足`CopyOnWrite` 的 ArrayList,所谓`CopyOnWrite` 也就是说:在计算机,如果你想要对一块内存进行修改时,我们不在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了。 +从 `CopyOnWriteArrayList` 的名字就能看出 `CopyOnWriteArrayList` 是满足 `CopyOnWrite` 的。所谓 `CopyOnWrite` 也就是说:在计算机,如果你想要对一块内存进行修改时,我们不在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了。 -### 3.3 CopyOnWriteArrayList 读取和写入源码简单分析 +### CopyOnWriteArrayList 读取和写入源码简单分析 -#### 3.3.1 CopyOnWriteArrayList 读取操作的实现 +#### CopyOnWriteArrayList 读取操作的实现 -读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,只会被另外一个 array 替换,因此可以保证数据安全。 +读取操作没有任何同步控制和锁操作,理由就是内部数组 `array` 不会发生修改,只会被另外一个 `array` 替换,因此可以保证数据安全。 ```java /** The array, accessed only via getArray/setArray. */ @@ -86,7 +60,7 @@ implements List, RandomAccess, Cloneable, Serializable #### 3.3.2 CopyOnWriteArrayList 写入操作的实现 -CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁,保证了同步,避免了多线程写的时候会 copy 出多个副本出来。 +`CopyOnWriteArrayList` 写入操作 `add()`方法在添加集合的时候加了锁,保证了同步,避免了多线程写的时候会 copy 出多个副本出来。 ```java /** @@ -111,41 +85,49 @@ CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁 } ``` -## 四 ConcurrentLinkedQueue +## ConcurrentLinkedQueue -Java 提供的线程安全的 Queue 可以分为**阻塞队列**和**非阻塞队列**,其中阻塞队列的典型例子是 BlockingQueue,非阻塞队列的典型例子是 ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 **阻塞队列可以通过加锁来实现,非阻塞队列可以通过 CAS 操作实现。** +Java 提供的线程安全的 `Queue` 可以分为**阻塞队列**和**非阻塞队列**,其中阻塞队列的典型例子是 `BlockingQueue`,非阻塞队列的典型例子是 `ConcurrentLinkedQueue`,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 **阻塞队列可以通过加锁来实现,非阻塞队列可以通过 CAS 操作实现。** -从名字可以看出,`ConcurrentLinkedQueue`这个队列使用链表作为其数据结构.ConcurrentLinkedQueue 应该算是在高并发环境中性能最好的队列了。它之所有能有很好的性能,是因为其内部复杂的实现。 +从名字可以看出,`ConcurrentLinkedQueue`这个队列使用链表作为其数据结构.`ConcurrentLinkedQueue` 应该算是在高并发环境中性能最好的队列了。它之所有能有很好的性能,是因为其内部复杂的实现。 -ConcurrentLinkedQueue 内部代码我们就不分析了,大家知道 ConcurrentLinkedQueue 主要使用 CAS 非阻塞算法来实现线程安全就好了。 +`ConcurrentLinkedQueue` 内部代码我们就不分析了,大家知道 `ConcurrentLinkedQueue` 主要使用 CAS 非阻塞算法来实现线程安全就好了。 -ConcurrentLinkedQueue 适合在对性能要求相对较高,同时对队列的读写存在多个线程同时进行的场景,即如果对队列加锁的成本较高则适合使用无锁的 ConcurrentLinkedQueue 来替代。 +`ConcurrentLinkedQueue` 适合在对性能要求相对较高,同时对队列的读写存在多个线程同时进行的场景,即如果对队列加锁的成本较高则适合使用无锁的 `ConcurrentLinkedQueue` 来替代。 -## 五 BlockingQueue +## BlockingQueue -### 5.1 BlockingQueue 简单介绍 +### BlockingQueue 简介 -上面我们己经提到了 ConcurrentLinkedQueue 作为高性能的非阻塞队列。下面我们要讲到的是阻塞队列——BlockingQueue。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是 BlockingQueue 提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。 +上面我们己经提到了 `ConcurrentLinkedQueue` 作为高性能的非阻塞队列。下面我们要讲到的是阻塞队列——`BlockingQueue`。阻塞队列(`BlockingQueue`)被广泛使用在“生产者-消费者”问题中,其原因是 `BlockingQueue` 提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。 -BlockingQueue 是一个接口,继承自 Queue,所以其实现类也可以作为 Queue 的实现来使用,而 Queue 又继承自 Collection 接口。下面是 BlockingQueue 的相关实现类: +`BlockingQueue` 是一个接口,继承自 `Queue`,所以其实现类也可以作为 `Queue` 的实现来使用,而 `Queue` 又继承自 `Collection` 接口。下面是 `BlockingQueue` 的相关实现类: ![BlockingQueue 的实现类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/51622268.jpg) -**下面主要介绍一下:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,这三个 BlockingQueue 的实现类。** +下面主要介绍一下 3 个常见的 `BlockingQueue` 的实现类:`ArrayBlockingQueue`、`LinkedBlockingQueue` 、`PriorityBlockingQueue` 。 + +### ArrayBlockingQueue + +`ArrayBlockingQueue` 是 `BlockingQueue` 接口的有界队列实现类,底层采用数组来实现。 -### 5.2 ArrayBlockingQueue +```java +public class ArrayBlockingQueue +extends AbstractQueue +implements BlockingQueue, Serializable{} +``` -**ArrayBlockingQueue** 是 BlockingQueue 接口的有界队列实现类,底层采用**数组**来实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。 +`ArrayBlockingQueue` 一旦创建,容量不能改变。其并发控制采用可重入锁 `ReentrantLock` ,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。 -ArrayBlockingQueue 默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 ArrayBlockingQueue。而非公平性则是指访问 ArrayBlockingQueue 的顺序不是遵守严格的时间顺序,有可能存在,当 ArrayBlockingQueue 可以被访问时,长时间阻塞的线程依然无法访问到 ArrayBlockingQueue。如果保证公平性,通常会降低吞吐量。如果需要获得公平性的 ArrayBlockingQueue,可采用如下代码: +`ArrayBlockingQueue` 默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 `ArrayBlockingQueue`。而非公平性则是指访问 `ArrayBlockingQueue` 的顺序不是遵守严格的时间顺序,有可能存在,当 `ArrayBlockingQueue` 可以被访问时,长时间阻塞的线程依然无法访问到 `ArrayBlockingQueue`。如果保证公平性,通常会降低吞吐量。如果需要获得公平性的 `ArrayBlockingQueue`,可采用如下代码: ```java private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(10,true); ``` -### 5.3 LinkedBlockingQueue +### LinkedBlockingQueue -**LinkedBlockingQueue** 底层基于**单向链表**实现的阻塞队列,可以当做无界队列也可以当做有界队列来使用,同样满足 FIFO 的特性,与 ArrayBlockingQueue 相比起来具有更高的吞吐量,为了防止 LinkedBlockingQueue 容量迅速增,损耗大量内存。通常在创建 LinkedBlockingQueue 对象时,会指定其大小,如果未指定,容量等于 Integer.MAX_VALUE。 +`LinkedBlockingQueue` 底层基于**单向链表**实现的阻塞队列,可以当做无界队列也可以当做有界队列来使用,同样满足 FIFO 的特性,与 `ArrayBlockingQueue` 相比起来具有更高的吞吐量,为了防止 `LinkedBlockingQueue` 容量迅速增,损耗大量内存。通常在创建 `LinkedBlockingQueue` 对象时,会指定其大小,如果未指定,容量等于 `Integer.MAX_VALUE` 。 **相关构造方法:** @@ -174,25 +156,21 @@ private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueu } ``` -### 5.4 PriorityBlockingQueue - -**PriorityBlockingQueue** 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 `compareTo()` 方法来指定元素排序规则,或者初始化时通过构造器参数 `Comparator` 来指定排序规则。 +### PriorityBlockingQueue -PriorityBlockingQueue 并发控制采用的是 **ReentrantLock**,队列为无界队列(ArrayBlockingQueue 是有界队列,LinkedBlockingQueue 也可以通过在构造函数中传入 capacity 指定队列最大的容量,但是 PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,**如果空间不够的话会自动扩容**)。 +`PriorityBlockingQueue` 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 `compareTo()` 方法来指定元素排序规则,或者初始化时通过构造器参数 `Comparator` 来指定排序规则。 -简单地说,它就是 PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。 +`PriorityBlockingQueue` 并发控制采用的是可重入锁 `ReentrantLock`,队列为无界队列(`ArrayBlockingQueue` 是有界队列,`LinkedBlockingQueue` 也可以通过在构造函数中传入 `capacity` 指定队列最大的容量,但是 `PriorityBlockingQueue` 只能指定初始的队列大小,后面插入元素的时候,**如果空间不够的话会自动扩容**)。 -**推荐文章:** +简单地说,它就是 `PriorityQueue` 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 `ClassCastException` 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。 -《解读 Java 并发队列 BlockingQueue》 +**推荐文章:** [《解读 Java 并发队列 BlockingQueue》](https://javadoop.com/post/java-concurrent-queue) -[https://javadoop.com/post/java-concurrent-queue](https://javadoop.com/post/java-concurrent-queue "https://javadoop.com/post/java-concurrent-queue") - -## 六 ConcurrentSkipListMap +## ConcurrentSkipListMap 下面这部分内容参考了极客时间专栏[《数据结构与算法之美》](https://time.geekbang.org/column/intro/126?code=zl3GYeAsRI4rEJIBNu5B/km7LSZsPDlGWQEpAYw5Vu0=&utm_term=SPoster "《数据结构与算法之美》")以及《实战 Java 高并发程序设计》。 -**为了引出 ConcurrentSkipListMap,先带着大家简单理解一下跳表。** +为了引出 `ConcurrentSkipListMap`,先带着大家简单理解一下跳表。 对于一个单链表,即使链表是有序的,如果我们想要在其中查找某个数据,也只能从头到尾遍历链表,这样效率自然就会很低,跳表就不一样了。跳表是一种可以用来快速查找的数据结构,有点类似于平衡树。它们都可以对元素进行快速的查找。但一个重要的区别是:对平衡树的插入和删除往往很可能导致平衡树进行一次全局的调整。而对跳表的插入和删除只需要对整个数据结构的局部进行操作即可。这样带来的好处是:在高并发的情况下,你会需要一个全局锁来保证整个平衡树的线程安全。而对于跳表,你只需要部分锁即可。这样,在高并发环境下,你就可以拥有更好的性能。而就查询的性能而言,跳表的时间复杂度也是 **O(logn)** 所以在并发数据结构中,JDK 使用跳表来实现一个 Map。 @@ -210,20 +188,10 @@ PriorityBlockingQueue 并发控制采用的是 **ReentrantLock**,队列为无 从上面很容易看出,**跳表是一种利用空间换时间的算法。** -使用跳表实现 Map 和使用哈希算法实现 Map 的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果。所以,如果你的应用需要有序性,那么跳表就是你不二的选择。JDK 中实现这一数据结构的类是 ConcurrentSkipListMap。 +使用跳表实现 `Map` 和使用哈希算法实现 `Map` 的另外一个不同之处是:哈希并不会保存元素的顺序,而跳表内所有的元素都是排序的。因此在对跳表进行遍历时,你会得到一个有序的结果。所以,如果你的应用需要有序性,那么跳表就是你不二的选择。JDK 中实现这一数据结构的类是 `ConcurrentSkipListMap`。 -## 七 参考 +## 参考 - 《实战 Java 高并发程序设计》 - https://javadoop.com/post/java-concurrent-queue - https://juejin.im/post/5aeebd02518825672f19c546 - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java 面试突击》:** 由本文档衍生的专为面试而生的《Java 面试突击》V2.0 PDF 版本[公众号](#公众号 "公众号")后台回复 **"面试突击"** 即可免费领取! - -**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git "a/docs/java/new-features/java\346\226\260\347\211\271\346\200\247\346\200\273\347\273\223.md" "b/docs/java/new-features/java\346\226\260\347\211\271\346\200\247\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..c815fa1ab7d --- /dev/null +++ "b/docs/java/new-features/java\346\226\260\347\211\271\346\200\247\346\200\273\347\273\223.md" @@ -0,0 +1,948 @@ +Java 8 新特性见这里:[Java8 新特性最佳指南](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484744&idx=1&sn=9db31dca13d327678845054af75efb74&chksm=cea24a83f9d5c3956f4feb9956b068624ab2fdd6c4a75fe52d5df5dca356a016577301399548&token=1082669959&lang=zh_CN&scene=21#wechat_redirect) 。 + +你可以在 [Archived OpenJDK General-Availability Releases](http://jdk.java.net/archive/) 上下载自己需要的 JDK 版本! + +官方的新特性说明文档地址: https://openjdk.java.net/projects/jdk/ 。 + +_Guide 哥:别人家的特性都用了几年了,我 Java 才出来,哈哈!真实!_ + +## Java9 + +发布于 2017 年 9 月 21 日 。作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、Stream 流 + +### Java 平台模块系统 + +Java 平台模块系统是[Jigsaw Project](https://openjdk.java.net/projects/jigsaw/)的一部分,把模块化开发实践引入到了 Java 平台中,可以让我们的代码可重用性更好! + +什么是模块系统?官方的定义是:A uniquely named, reusable group of related packages, as well as resources (such as images and XML files) and a module descriptor. + +简单来说,你可以将一个模块看作是一组唯一命名、可重用的包、资源和模块描述文件(module-info.java)。 + +任意一个 jar 文件,只要加上一个 模块描述文件(module-info.java),就可以升级为一个模块。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/module-structure.png) + +在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小。 + +我们可以通过 exports 关键词精准控制哪些类可以对外开放使用,哪些类只能内部使用。 + +```java +module my.module { + //exports 公开指定包的所有公共成员 + exports com.my.package.name; +} + +module my.module { + //exports…to 限制访问的成员范围 + export com.my.package.name to com.specific.package; +} +``` + +Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 `module-info.java` 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。 + +想要深入了解 Java 9 的模块化,参见: + +- [《Project Jigsaw: Module System Quick-Start Guide》](https://openjdk.java.net/projects/jigsaw/quick-start) +- [《Java 9 Modules: part 1》](https://stacktraceguru.com/java9/module-introduction) + +### Jshell + +jshell 是 Java 9 新增的一个实用工具。为 Java 提供了类似于 Python 的实时命令行交互工具。 + +在 Jshell 中可以直接输入表达式并查看其执行结果。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816083417616.png) + +### 集合增强 + +增加 了 `List.of()`、`Set.of()`、`Map.of()` 和 `Map.ofEntries()`等工厂方法来创建不可变集合(这部分内容有点参考 Guava 的味道) + +```java +List.of("Java", "C++"); +Set.of("Java", "C++"); +Map.of("Java", 1, "C++", 2)`; +``` + +使用 `of()` 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 `java.lang.UnsupportedOperationException` 异常。 + +`Collectors` 中增加了新的方法 `filtering()` 和 `flatMapping()`。 + +`Collectors` 的 `filtering()` 方法类似于 `Stream` 类的 `filter()` 方法,都是用于过滤元素。 + +> Java 8 为 `Collectors` 类引入了 `groupingBy` 操作,用于根据特定的属性将对象分组。 + +```java +List list = List.of("x","www", "yy", "zz"); +Map> result = list.stream() + .collect(Collectors.groupingBy(String::length, + Collectors.filtering(s -> !s.contains("z"), + Collectors.toList()))); + +System.out.println(result); // {1=[x], 2=[yy], 3=[www]} +``` + +### Stream & Optional 增强 + +`Stream` 中增加了新的方法 `ofNullable()`、`dropWhile()`、`takeWhile()` 以及 `iterate()` 方法的重载方法。 + +Java 9 中的 `ofNullable()` 方 法允许我们创建一个单元素的 `Stream`,可以包含一个非空元素,也可以创建一个空 `Stream`。 而在 Java 8 中则不可以创建空的 `Stream` 。 + +```java +Stream stringStream = Stream.ofNullable("Java"); +System.out.println(stringStream.count());// 1 +Stream nullStream = Stream.ofNullable(null); +System.out.println(nullStream.count());//0 +``` + +`takeWhile()` 方法可以从 `Stream` 中依次获取满足条件的元素,直到不满足条件为止结束获取。 + +```java +List integerList = List.of(11, 33, 66, 8, 9, 13); +integerList.stream().takeWhile(x -> x < 50).forEach(System.out::println);// 11 33 +``` + +`dropWhile()` 方法的效果和 `takeWhile()` 相反。 + +```java +List integerList2 = List.of(11, 33, 66, 8, 9, 13); +integerList2.stream().dropWhile(x -> x < 50).forEach(System.out::println);// 66 8 9 13 +``` + +`iterate()` 方法的新重载方法提供了一个 `Predicate` 参数 (判断条件)来决定什么时候结束迭代 + +```java +public static Stream iterate(final T seed, final UnaryOperator f) { +} +// 新增加的重载方法 +public static Stream iterate(T seed, Predicate hasNext, UnaryOperator next) { + +} +``` + +两者的使用对比如下,新的 `iterate()` 重载方法更加灵活一些。 + +```java +// 使用原始 iterate() 方法输出数字 1~10 +Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println); +// 使用新的 iterate() 重载方法输出数字 1~10 +Stream.iterate(1, i -> i <= 10, i -> i + 1).forEach(System.out::println); +``` + +`Optional` 类中新增了 `ifPresentOrElse()`、`or()` 和 `stream()` 等方法 + +`ifPresentOrElse()` 方法接受两个参数 `Consumer` 和 `Runnable` ,如果 `Optional` 不为空调用 `Consumer` 参数,为空则调用 `Runnable` 参数。 + +```java +public void ifPresentOrElse(Consumer action, Runnable emptyAction) + +Optional objectOptional = Optional.empty(); +objectOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Empty!!!"));// Empty!!! +``` + +`or()` 方法接受一个 `Supplier` 参数 ,如果 `Optional` 为空则返回 `Supplier` 参数指定的 `Optional` 值。 + +```java +public Optional or(Supplier> supplier) + +Optional objectOptional = Optional.empty(); +objectOptional.or(() -> Optional.of("java")).ifPresent(System.out::println);//java +``` + +### String 存储结构变更 + +JDK 8 及之前的版本,`String` 一直是用 `char[]` 存储。在 Java 9 之后,`String` 的实现改用 `byte[]` 数组存储字符串。 + +### 进程 API + +Java 9 增加了 `ProcessHandle` 接口,可以对原生进程进行管理,尤其适合于管理长时间运行的进程。 + +```java +System.out.println(ProcessHandle.current().pid()); +System.out.println(ProcessHandle.current().info()); +``` + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816104614414.png) + +### 平台日志 API 和服务 + +Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.LoggerFinder` 用来管理 JDK 使 用的日志记录器实现。JVM 在运行时只有一个系统范围的 `LoggerFinder` 实例。 + +我们可以通过添加自己的 `System.LoggerFinder` 实现来让 JDK 和应用使用 SLF4J 等其他日志记录框架。 + +### 反应式流 ( Reactive Streams ) + +在 Java9 中的 `java.util.concurrent.Flow` 类中新增了反应式流规范的核心接口 。 + +`Flow` 中包含了 `Flow.Publisher`、`Flow.Subscriber`、`Flow.Subscription` 和 `Flow.Processor` 等 4 个核心接口。Java 9 还提供了`SubmissionPublisher` 作为`Flow.Publisher` 的一个实现。 + +### 变量句柄 + +变量句柄是一个变量或一组变量的引用,包括静态域,非静态域,数组元素和堆外数据结构中的组成部分等 + +变量句柄的含义类似于已有的方法句柄 `MethodHandle` ,由 Java 类 `java.lang.invoke.VarHandle` 来表示,可以使用类 `java.lang.invoke.MethodHandles.Lookup` 中的静态工厂方法来创建 `VarHandle` 对象。 + +`VarHandle` 的出现替代了 `java.util.concurrent.atomic` 和 `sun.misc.Unsafe` 的部分操作。并且提供了一系列标准的内存屏障操作,用于更加细粒度的控制内存排序。在安全性、可用性、性能上都要优于现有的 API。 + +### 改进方法句柄(Method Handle) + +方法句柄从 Java7 开始引入,Java9 在类`java.lang.invoke.MethodHandles` 中新增了更多的静态方法来创建不同类型的方法句柄。 + +### 接口私有方法 + +Java 9 允许在接口中使用私有方法。 + +```java +public interface MyInterface { + private void methodPrivate(){ + + } +} +``` + +### Java9 其它新特性 + +- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章:[《Effectively Final Variables in Java》](https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html) +- 类 `CompletableFuture` 中增加了几个新的方法(`completeAsync` ,`orTimeout` 等) +- **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性(Java 11 中已经被弃用)。 +- **I/O 流的新特性** :增加了新的方法来读取和复制 `InputStream` 中包含的数据 +- **改进应用的安全性能** :Java 9 新增了 4 个 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512 +- ...... + +## Java10 + +发布于 2018 年 3 月 20 日,最知名的特性应该是 var 关键字(局部变量类型推断)的引入了,其他还有垃圾收集器改善、GC 改进、性能提升、线程管控等一批新特性 + +### var(局部变量推断) + +由于太多 Java 开发者希望 Java 中引入局部变量推断,于是 Java 10 的时候它来了,也算是众望所归了! + +Java 10 提供了 var 关键字声明局部变量。 + +> Scala 和 Kotlin 中有 val 关键字 ( `final var` 组合关键字),Java10 中并没有引入。 + +Java 10 只引入了 var,而 + +```java +var id = 0; +var codefx = new URL("https://mp.weixin.qq.com/"); +var list = new ArrayList<>(); +var list = List.of(1, 2, 3); +var map = new HashMap(); +var p = Paths.of("src/test/java/Java9FeaturesTest.java"); +var numbers = List.of("a", "b", "c"); +for (var n : list) + System.out.print(n+ " "); +``` + +var 关键字只能用于带有构造器的局部变量和 for 循环中。 + +```java +var count=null; //❌编译不通过,不能声明为 null +var r = () -> Math.random();//❌编译不通过,不能声明为 Lambda表达式 +var array = {1,2,3};//❌编译不通过,不能声明数组 +``` + +var 并不会改变 Java 是一门静态类型语言的事实,编译器负责推断出类型。 + +相关阅读:[《Java 10 新特性之局部变量类型推断》](https://zhuanlan.zhihu.com/p/34911982)。 + +### 集合增强 + +`list`,`set`,`map` 提供了静态方法`copyOf()`返回入参集合的一个不可变拷贝。 + +以下为 JDK 的源码: + +```java +static List copyOf(Collection coll) { + return ImmutableCollections.listCopy(coll); +} +``` + +使用 `copyOf()` 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 `java.lang.UnsupportedOperationException` 异常。 IDEA 也会有相应的提示。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816154125579.png) + +`java.util.stream.Collectors` 中新增了静态方法,用于将流中的元素收集为不可变的集合。 + +```java +var list = new ArrayList<>(); +list.stream().collect(Collectors.toUnmodifiableList()); +list.stream().collect(Collectors.toUnmodifiableSet()); +``` + +### Optional + +新增了`orElseThrow()`方法来在没有值时抛出指定的异常。 + +```java +Optional.ofNullable(cache.getIfPresent(key)) + .orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key)); +``` + +### 并行全垃圾回收器 G1 + +从 Java9 开始 G1 就了默认的垃圾回收器,G1 是以一种低延时的垃圾回收器来设计的,旨在避免进行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用单线程去完成标记清除算法,这可能会导致垃圾回收期在无法回收内存的时候触发 Full GC。 + +为了最大限度地减少 Full GC 造成的应用停顿的影响,从 Java10 开始,G1 的 FullGC 改为并行的标记清除算法,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了 Full GC 的发生,以带来更好的性能提升、更大的吞吐量。 + +### 应用程序类数据共享(扩展 CDS 功能) + +在 Java 5 中就已经引入了类数据共享机制 (Class Data Sharing,简称 CDS),允许将一组类预处理为共享归档文件,以便在运行时能够进行内存映射以减少 Java 程序的启动时间,当多个 Java 虚拟机(JVM)共享相同的归档文件时,还可以减少动态内存的占用量,同时减少多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用。CDS 在当时还是 Oracle JDK 的商业特性。 + +Java 10 在现有的 CDS 功能基础上再次拓展,以允许应用类放置在共享存档中。CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS 为 (Application Class-Data Sharing,AppCDS) 支持,大大加大了 CDS 的适用范围。其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升。 + +### Java10 其他新特性 + +- **线程-局部管控**:Java 10 中线程管控引入 JVM 安全点的概念,将允许在不运行全局 JVM 安全点的情况下实现线程回调,由线程本身或者 JVM 线程来执行,同时保持线程处于阻塞状态,这种方式使得停止单个线程变成可能,而不是只能启用或停止所有线程 +- **备用存储装置上的堆分配**:Java 10 中将使得 JVM 能够使用适用于不同类型的存储机制的堆,在可选内存设备上进行堆内存分配 +- **统一的垃圾回收接口**:Java 10 中,hotspot/gc 代码实现方面,引入一个干净的 GC 接口,改进不同 GC 源代码的隔离性,多个 GC 之间共享的实现细节代码应该存在于辅助类中。统一垃圾回收接口的主要原因是:让垃圾回收器(GC)这部分代码更加整洁,便于新人上手开发,便于后续排查相关问题。 +- ...... + +## Java11 + +Java11 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本!Java 11 和 2017 年 9 月份发布的 Java 9 以及 2018 年 3 月份发布的 Java 10 相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,**Oracle 表示会对 Java 11 提供大力支持,这一支持将会持续至 2026 年 9 月。这是据 Java 8 以后支持的首个长期版本。** + +![](https://img-blog.csdnimg.cn/20210603202746605.png) + +### String + +Java 11 增加了一系列的字符串处理方法,如以下所示。 + +_Guide:说白点就是多了层封装,JDK 开发组的人没少看市面上常见的工具类框架啊!_ + +```java +//判断字符串是否为空 +" ".isBlank();//true +//去除字符串首尾空格 +" Java ".strip();// "Java" +//去除字符串首部空格 +" Java ".stripLeading(); // "Java " +//去除字符串尾部空格 +" Java ".stripTrailing(); // " Java" +//重复字符串多少次 +"Java".repeat(3); // "JavaJavaJava" + +//返回由行终止符分隔的字符串集合。 +"A\nB\nC".lines().count(); // 3 +"A\nB\nC".lines().collect(Collectors.toList()); +``` + +### Optional + +新增了`empty()`方法来判断指定的 `Optional` 对象是否为空。 + +```java +var op = Optional.empty(); +System.out.println(op.isEmpty());//判断指定的 Optional 对象是否为空 +``` + +### ZGC(可伸缩低延迟垃圾收集器) + +**ZGC 即 Z Garbage Collector**,是一个可伸缩的、低延迟的垃圾收集器。 + +ZGC 主要为了满足如下目标进行设计: + +- GC 停顿时间不超过 10ms +- 即能处理几百 MB 的小堆,也能处理几个 TB 的大堆 +- 应用吞吐能力不会下降超过 15%(与 G1 回收算法相比) +- 方便在此基础上引入新的 GC 特性和利用 colord +- 针以及 Load barriers 优化奠定基础 +- 当前只支持 Linux/x64 位平台 + +ZGC 目前 **处在实验阶段**,只支持 Linux/x64 平台。 + +与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。 + +在 ZGC 中出现 Stop The World 的情况会更少! + +详情可以看 : [《新一代垃圾回收器 ZGC 的探索与实践》](https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html) + +### 标准 HTTP Client 升级 + +Java 11 对 Java 9 中引入并在 Java 10 中进行了更新的 Http Client API 进行了标准化,在前两个版本中进行孵化的同时,Http Client 几乎被完全重写,并且现在完全支持异步非阻塞。 + +并且,Java11 中,Http Client 的包名由 `jdk.incubator.http` 改为`java.net.http`,该 API 通过 `CompleteableFuture` 提供非阻塞请求和响应语义。使用起来也很简单,如下: + +```java +var request = HttpRequest.newBuilder() + .uri(URI.create("https://javastack.cn")) + .GET() + .build(); +var client = HttpClient.newHttpClient(); + +// 同步 +HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); +System.out.println(response.body()); + +// 异步 +client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenAccept(System.out::println); +``` + +### var(Lambda 参数的局部变量语法) + +从 Java 10 开始,便引入了局部变量类型推断这一关键特性。类型推断允许使用关键字 var 作为局部变量的类型而不是实际类型,编译器根据分配给变量的值推断出类型。 + +Java 10 中对 var 关键字存在几个限制 + +- 只能用于局部变量上 +- 声明时必须初始化 +- 不能用作方法参数 +- 不能在 Lambda 表达式中使用 + +Java11 开始允许开发者在 Lambda 表达式中使用 var 进行参数声明。 + +```java +// 下面两者是等价的 +Consumer consumer = (var i) -> System.out.println(i); +Consumer consumer = (String i) -> System.out.println(i); +``` + +### 启动单文件源代码程序 + +[JEP 330:启动单文件源代码程序(aunch Single-File Source-Code Programs)](https://openjdk.java.net/jeps/330) 可以让我们运行单一文件的 Java 源代码。此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行,不需要在磁盘上生成 `.class` 文件了。 + +唯一的约束在于所有相关的类必须定义在同一个 Java 文件中。 + +对于 Java 初学者并希望尝试简单程序的人特别有用,并且能和 jshell 一起使用 + +一定能程度上增强了使用 Java 来写脚本程序的能力。 + +### Java11 其他新特性 + +- **新的垃圾回收器 Epsilon** :一个完全消极的 GC 实现,分配有限的内存资源,最大限度的降低内存占用和内存吞吐延迟时间 +- **低开销的 Heap Profiling** :Java 11 中提供一种低开销的 Java 堆分配采样方法,能够得到堆分配的 Java 对象信息,并且能够通过 JVMTI 访问堆信息 +- **TLS1.3 协议** :Java 11 中包含了传输层安全性(TLS)1.3 规范(RFC 8446)的实现,替换了之前版本中包含的 TLS,包括 TLS 1.2,同时还改进了其他 TLS 功能,例如 OCSP 装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627),在安全性和性能方面也做了很多提升 +- **飞行记录器(Java Flight Recorder)** :飞行记录器之前是商业版 JDK 的一项分析工具,但在 Java 11 中,其代码被包含到公开代码库中,这样所有人都能使用该功能了。 +- ...... + +## Java12 + +### String + +Java 11 增加了两个的字符串处理方法,如以下所示。 + +`indent()` 方法可以实现字符串缩进。 + +```java +String text = "Java"; +// 缩进 4 格 +text = text.indent(4); +System.out.println(text); +text = text.indent(-10); +System.out.println(text); +``` + +输出: + +``` + Java +Java +``` + +`transform()` 方法可以用来转变指定字符串。 + +```java +String result = "foo".transform(input -> input + " bar"); +System.out.println(result); // foo bar +``` + +### 文件比较 + +Java 12 添加了以下方法来比较两个文件: + +```java +public static long mismatch(Path path, Path path2) throws IOException +``` + +`mismatch()` 方法用于比较两个文件,并返回第一个不匹配字符的位置,如果文件相同则返回 -1L。 + +代码示例(两个文件内容相同的情况): + +```java +Path filePath1 = Files.createTempFile("file1", ".txt"); +Path filePath2 = Files.createTempFile("file2", ".txt"); +Files.writeString(filePath1, "Java 12 Article"); +Files.writeString(filePath2, "Java 12 Article"); + +long mismatch = Files.mismatch(filePath1, filePath2); +assertEquals(-1, mismatch); +``` + +代码示例(两个文件内容不相同的情况): + +```java +Path filePath3 = Files.createTempFile("file3", ".txt"); +Path filePath4 = Files.createTempFile("file4", ".txt"); +Files.writeString(filePath3, "Java 12 Article"); +Files.writeString(filePath4, "Java 12 Tutorial"); + +long mismatch = Files.mismatch(filePath3, filePath4); +assertEquals(8, mismatch); +``` + +### 数字格式化工具类 + +`NumberFormat` 新增了对复杂的数字进行格式化的支持 + +```java +NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); +String result = fmt.format(1000); + System.out.println(result); // 输出为 1K,计算工资是多少K更方便了。。。 +``` + +### Shenandoah GC + +Redhat 主导开发的 Pauseless GC 实现,主要目标是 99.9% 的暂停小于 10ms,暂停与堆大小无关等 + +和 Java11 开源的 ZGC 相比(需要升级到 JDK11 才能使用),Shenandoah GC 有稳定的 JDK8u 版本,在 Java8 占据主要市场份额的今天有更大的可落地性。 + +### G1 收集器提升 + +Java12 为默认的垃圾收集器 G1 带来了两项更新: + +- **可中止的混合收集集合** :JEP344 的实现,为了达到用户提供的停顿时间目标,JEP 344 通过把要被回收的区域集(混合收集集合)拆分为强制和可选部分,使 G1 垃圾回收器能中止垃圾回收过程。 G1 可以中止可选部分的回收以达到停顿时间目标 +- **及时返回未使用的已分配内存** :JEP346 的实现,增强 G1 GC,以便在空闲时自动将 Java 堆内存返回给操作系统 + +### 预览新特性 + +作为预览特性加入,需要在`javac`编译和`java`运行时增加参数`--enable-preview` 。 + +#### 增强 Switch + +传统的 `switch` 语法存在容易漏写 `break` 的问题,而且从代码整洁性层面来看,多个 break 本质也是一种重复 + +Java12 增强了 `swtich` 表达式,使用类似 lambda 语法条件匹配成功后的执行块,不需要多写 break 。 + +```java +switch (day) { + case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); + case TUESDAY -> System.out.println(7); + case THURSDAY, SATURDAY -> System.out.println(8); + case WEDNESDAY -> System.out.println(9); +} +``` + +#### instanceof 模式匹配 + +`instanceof` 主要在**类型强转前探测对象的具体类型**。 + +之前的版本中,我们需要显示地对对象进行类型转换。 + +```java +Object obj = "我是字符串"; +if(obj instanceof String){ + String str = (String) obj; + System.out.println(str); +} +``` + +新版的 `instanceof` 可以在判断是否属于具体的类型同时完成转换。 + +```java +Object obj = "我是字符串"; +if(obj instanceof String str){ + System.out.println(str); +} +``` + +## Java13 + +### 增强 ZGC(释放未使用内存) + +在 Java 11 中是实验性的引入的 ZGC 在实际的使用中存在未能主动将未使用的内存释放给操作系统的问题。 + +ZGC 堆由一组称为 ZPages 的堆区域组成。在 GC 周期中清空 ZPages 区域时,它们将被释放并返回到页面缓存 **ZPageCache** 中,此缓存中的 ZPages 按最近最少使用(LRU)的顺序,并按照大小进行组织。 + +在 Java 13 中,ZGC 将向操作系统返回被标识为长时间未使用的页面,这样它们将可以被其他进程重用。 + +### SocketAPI 重构 + +Java Socket API 终于迎来了重大更新! + +Java 13 将 Socket API 的底层进行了重写, `NioSocketImpl` 是对 `PlainSocketImpl` 的直接替代,它使用 `java.util.concurrent` 包下的锁而不是同步方法。如果要使用旧实现,请使用 `-Djdk.net.usePlainSocketImpl=true`。 + +并且,在 Java 13 中是默认使用新的 Socket 实现。 + +```java +public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl { +} +``` + +### FileSystems + +`FileSystems` 类中添加了以下三种新方法,以便更容易地使用将文件内容视为文件系统的文件系统提供程序: + +- `newFileSystem(Path)` +- `newFileSystem(Path, Map)` +- `newFileSystem(Path, Map, ClassLoader)` + +### 动态 CDS 存档 + +Java 13 中对 Java 10 中引入的应用程序类数据共享(AppCDS)进行了进一步的简化、改进和扩展,即:**允许在 Java 应用程序执行结束时动态进行类归档**,具体能够被归档的类包括所有已被加载,但不属于默认基层 CDS 的应用程序类和引用类库中的类。 + +这提高了应用程序类数据共享([AppCDS](https://openjdk.java.net/jeps/310))的可用性。无需用户进行试运行来为每个应用程序创建类列表。 + +```bash +$ java -XX:ArchiveClassesAtExit=my_app_cds.jsa -cp my_app.jar +$ java -XX:SharedArchiveFile=my_app_cds.jsa -cp my_app.jar +``` + +### 预览新特性 + +#### 文本块 + +解决 Java 定义多行字符串时只能通过换行转义或者换行连接符来变通支持的问题,引入**三重双引号**来定义多行文本。 + +Java 13 支持两个 `"""` 符号中间的任何内容都会被解释为字符串的一部分,包括换行符。 + +未支持文本块之前的 HTML 写法: + +```java +String json ="{\n" + + " \"name\":\"mkyong\",\n" + + " \"age\":38\n" + + "}\n"; +``` + +支持文本块之后的 HTML 写法: + +```java + String json = """ + { + "name":"mkyong", + "age":38 + } + """; +``` + +未支持文本块之前的 SQL 写法: + +```sql +String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" + + "WHERE `CITY` = 'INDIANAPOLIS'\n" + + "ORDER BY `EMP_ID`, `LAST_NAME`;\n"; +``` + +支持文本块之后的 SQL 写法: + +```sql +String query = """ + SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` + WHERE `CITY` = 'INDIANAPOLIS' + ORDER BY `EMP_ID`, `LAST_NAME`; + """; +``` + +另外,`String` 类新增加了 3 个新的方法来操作文本块: + +- `formatted(Object... args)` :它类似于 `String` 的`format()`方法。添加它是为了支持文本块的格式设置。 +- `stripIndent()` :用于去除文本块中每一行开头和结尾的空格。 +- `translateEscapes()` :转义序列如 _“\\\t”_ 转换为 _“\t”_ + +由于文本块是一项预览功能,可以在未来版本中删除,因此这些新方法被标记为弃用。 + +```java +@Deprecated(forRemoval=true, since="13") +public String stripIndent() { +} +@Deprecated(forRemoval=true, since="13") +public String formatted(Object... args) { + +} +@Deprecated(forRemoval=true, since="13") +public String translateEscapes() { +} +``` + +#### 增强 Switch(引入 yield 关键字到 Switch 中) + +`Switch` 表达式中就多了一个关键字用于跳出 `Switch` 块的关键字 `yield`,主要用于返回一个值 + +`yield`和 `return` 的区别在于:`return` 会直接跳出当前循环或者方法,而 `yield` 只会跳出当前 `Switch` 块,同时在使用 `yield` 时,需要有 `default` 条件 + +```java + private static String descLanguage(String name) { + return switch (name) { + case "Java": yield "object-oriented, platform independent and secured"; + case "Ruby": yield "a programmer's best friend"; + default: yield name +" is a good language"; + }; + } +``` + +## Java14 + +### 空指针异常精准提示 + +通过 JVM 参数中添加`-XX:+ShowCodeDetailsInExceptionMessages`,可以在空指针异常中获取更为详细的调用信息,更快的定位和解决问题。 + +```java +a.b.c.i = 99; // 假设这段代码会发生空指针 +``` + +Java 14 之前: + +```java +Exception in thread "main" java.lang.NullPointerException + at NullPointerExample.main(NullPointerExample.java:5) +``` + +Java 14 之后: + +```java + // 增加参数后提示的异常中很明确的告知了哪里为空导致 +Exception in thread "main" java.lang.NullPointerException: + Cannot read field 'c' because 'a.b' is null. + at Prog.main(Prog.java:5) +``` + +### switch 的增强(转正) + +Java12 引入的 switch(预览特性)在 Java14 变为正式版本,不需要增加参数来启用,直接在 JDK14 中就能使用。 + +Java12 为 switch 表达式引入了类似 lambda 语法条件匹配成功后的执行块,不需要多写 break ,Java13 提供了 `yield` 来在 block 中返回值。 + +```java +String result = switch (day) { + case "M", "W", "F" -> "MWF"; + case "T", "TH", "S" -> "TTS"; + default -> { + if(day.isEmpty()) + yield "Please insert a valid day."; + else + yield "Looks like a Sunday."; + } + + }; +System.out.println(result); +``` + +### 预览新特性 + +#### record 关键字 + +简化数据类的定义方式,使用 `record` 代替 `class` 定义的类,只需要声明属性,就可以在获得属性的访问方法,以及 `toString()`,`hashCode()`, `equals()`方法 + +类似于使用 `class` 定义类,同时使用了 lomobok 插件,并打上了`@Getter,@ToString,@EqualsAndHashCode`注解 + +```java +/** + * 这个类具有两个特征 + * 1. 所有成员属性都是final + * 2. 全部方法由构造方法,和两个成员属性访问器组成(共三个) + * 那么这种类就很适合使用record来声明 + */ +final class Rectangle implements Shape { + final double length; + final double width; + + public Rectangle(double length, double width) { + this.length = length; + this.width = width; + } + + double length() { return length; } + double width() { return width; } +} +/** + * 1. 使用record声明的类会自动拥有上面类中的三个方法 + * 2. 在这基础上还附赠了equals(),hashCode()方法以及toString()方法 + * 3. toString方法中包括所有成员属性的字符串表示形式及其名称 + */ +record Rectangle(float length, float width) { } +``` + +#### 文本块 + +Java14 中,文本块依然是预览特性,不过,其引入了两个新的转义字符: + +- `\` : 表示行尾,不引入换行符 +- `\s` :表示单个空格 + +```java +String str = "凡心所向,素履所往,生如逆旅,一苇以航。"; + +String str2 = """ + 凡心所向,素履所往, \ + 生如逆旅,一苇以航。"""; +System.out.println(str2);// 凡心所向,素履所往, 生如逆旅,一苇以航。 +String text = """ + java + c++\sphp + """; +System.out.println(text); +//输出: +java +c++ php +``` + +#### instanceof 增强 + +依然是**预览特性** ,Java 12 新特性中介绍过。 + +### Java14 其他特性 + +- 从 Java11 引入的 ZGC 作为继 G1 过后的下一代 GC 算法,从支持 Linux 平台到 Java14 开始支持 MacOS 和 Window(个人感觉是终于可以在日常开发工具中先体验下 ZGC 的效果了,虽然其实 G1 也够用) +- 移除了 CMS(Concurrent Mark Sweep) 垃圾收集器(功成而退) +- 新增了 jpackage 工具,标配将应用打成 jar 包外,还支持不同平台的特性包,比如 linux 下的`deb`和`rpm`,window 平台下的`msi`和`exe` + +## Java15 + +### CharSequence + +`CharSequence` 接口添加了一个默认方法 `isEmpty()` 来判断字符序列为空,如果是则返回 true。 + +```java +public interface CharSequence { + default boolean isEmpty() { + return this.length() == 0; + } +} +``` + +### TreeMap + +`TreeMap` 新引入了下面这些方法: + +- `putIfAbsent()` +- `computeIfAbsent()` +- `computeIfPresent()` +- `compute()` +- `merge()` + +### ZGC(转正) + +Java11 的时候 ,ZGC 还在试验阶段。 + +当时,ZGC 的出现让众多 Java 开发者看到了垃圾回收器的另外一种可能,因此备受关注。 + +经过多个版本的迭代,不断的完善和修复问题,ZGC 在 Java 15 已经可以正式使用了! + +不过,默认的垃圾回收器依然是 G1。你可以通过下面的参数启动 ZGC: + +```bash +$ java -XX:+UseZGC className +``` + +### EdDSA(数字签名算法) + +新加入了一个安全性和性能都更强的基于 Edwards-Curve Digital Signature Algorithm (EdDSA)实现的数字签名算法。 + +虽然其性能优于现有的 ECDSA 实现,不过,它并不会完全取代 JDK 中现有的椭圆曲线数字签名算法( ECDSA)。 + +```java +KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519"); +KeyPair kp = kpg.generateKeyPair(); + +byte[] msg = "test_string".getBytes(StandardCharsets.UTF_8); + +Signature sig = Signature.getInstance("Ed25519"); +sig.initSign(kp.getPrivate()); +sig.update(msg); +byte[] s = sig.sign(); + +String encodedString = Base64.getEncoder().encodeToString(s); +System.out.println(encodedString); +``` + +输出: + +``` +0Hc0lxxASZNvS52WsvnncJOH/mlFhnA8Tc6D/k5DtAX5BSsNVjtPF4R4+yMWXVjrvB2mxVXmChIbki6goFBgAg== +``` + +### 文本块(转正) + +在 Java 15 ,文本块是正式的功能特性了。 + +### 隐藏类(Hidden Classes) + +隐藏类是为框架(frameworks)所设计的,隐藏类不能直接被其他类的字节码使用,只能在运行时生成类并通过反射间接使用它们。 + +### 预览新特性 + +#### record 关键字 + +Java 15 对 Java 14 中引入的预览新特性进行了增强,主要是引入了一个新的概念 **密封类(Sealed Classes)。** + +密封类可以对继承或者实现它们的类进行限制。 + +比如抽象类 `Person` 只允许 `Employee` 和 `Manager` 继承。 + +```java +public abstract sealed class Person + permits Employee, Manager { + + //... +} +``` + +另外,任何扩展密封类的类本身都必须声明为 `sealed`、`non-sealed` 或 `final`。 + +```java +public final class Employee extends Person { +} + +public non-sealed class Manager extends Person { +} +``` + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javaguide/image-20210820153955587.png) + +在 `java.lang.Class` 增加了两个公共方法用于获取 `Record` 类信息: + +1. `RecordComponent[] getRecordComponents()` +2. `boolean isRecord()` + +#### instanceof 模式匹配 + +Java 15 并没有对此特性进行调整,继续预览特性,主要用于接受更多的使用反馈。 + +在未来的 Java 版本中,Java 的目标是继续完善 `instanceof` 模式匹配新特性。 + +### Java15 其他新特性 + +- **Nashorn JavaScript 引擎彻底移除** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性。在 Java 11 中就已经被弃用,到了 Java 15 就彻底被删除了。 +- **DatagramSocket API 重构** +- **禁用和废弃偏向锁(Biased Locking)** : 偏向锁的引入增加了 JVM 的复杂性大于其带来的性能提升。不过,你仍然可以使用 `-XX:+UseBiasedLocking` 启用偏向锁定,但它会提示 这是一个已弃用的 API。 +- ...... + +## 总结 + +### 关于预览特性 + +先贴一段 oracle 官网原文:`This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future JDK releases. To compile and run code that contains preview features, you must specify additional command-line options.` + +这是一个预览功能,该功能的设计,规格和实现是完整的,但不是永久性的,这意味着该功能可能以其他形式存在或在将来的 JDK 版本中根本不存在。 要编译和运行包含预览功能的代码,必须指定其他命令行选项。 + +就以`switch`的增强为例子,从 Java12 中推出,到 Java13 中将继续增强,直到 Java14 才正式转正进入 JDK 可以放心使用,不用考虑后续 JDK 版本对其的改动或修改 + +一方面可以看出 JDK 作为标准平台在增加新特性的严谨态度,另一方面个人认为是对于预览特性应该采取审慎使用的态度。特性的设计和实现容易,但是其实际价值依然需要在使用中去验证 + +### JVM 虚拟机优化 + +每次 Java 版本的发布都伴随着对 JVM 虚拟机的优化,包括对现有垃圾回收算法的改进,引入新的垃圾回收算法,移除老旧的不再适用于今天的垃圾回收算法等 + +整体优化的方向是**高效,低时延的垃圾回收表现** + +对于日常的应用开发者可能比较关注新的语法特性,但是从一个公司角度来说,在考虑是否升级 Java 平台时更加考虑的是**JVM 运行时的提升** + +## 参考资料 + +- JDK Project Overview : +- IBM Developer Java9 +- Guide to Java10 +- Java 10 新特性介绍 +- IBM Devloper Java11 +- Java 11 – Features and Comparison: +- Oracle Java12 ReleaseNote +- Oracle Java13 ReleaseNote +- New Features in Java 12 +- New Java13 Features +- Java13 新特性概述 +- Oracle Java14 record +- java14-features +- Java 14 Features : +- What is new in Java 15: https://mkyong.com/java/what-is-new-in-java-15/ \ No newline at end of file diff --git "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" "b/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" deleted file mode 100644 index 2d56e512255..00000000000 --- "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" +++ /dev/null @@ -1,441 +0,0 @@ -大家好,我是Guide哥!这篇文章来自读者的投稿,经过了两次较大的改动,两周的完善终于完成。Java 8新特性见这里:[Java8新特性最佳指南](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484744&idx=1&sn=9db31dca13d327678845054af75efb74&chksm=cea24a83f9d5c3956f4feb9956b068624ab2fdd6c4a75fe52d5df5dca356a016577301399548&token=1082669959&lang=zh_CN&scene=21#wechat_redirect) 。 - -*Guide 哥:别人家的特性都用了几年了,我 Java 才出来,哈哈!真实!* - -## Java9 - -发布于 2017 年 9 月 21 日 。作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、Stream 流 - -### Java 平台模块系统 - -Java 平台模块系统,也就是 Project Jigsaw,把模块化开发实践引入到了 Java 平台中。在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小。 - -Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。 - -### Jshell - -jshell 是 Java 9 新增的一个实用工具。为 Java 提供了类似于 Python 的实时命令行交互工具。 - -在 Jshell 中可以直接输入表达式并查看其执行结果 - -### 集合、Stream 和 Optional - -- 增加 了 `List.of()`、`Set.of()`、`Map.of()` 和 `Map.ofEntries()`等工厂方法来创建不可变集合,比如`List.of("Java", "C++");`、`Map.of("Java", 1, "C++", 2)`;(这部分内容有点参考 Guava 的味道) -- `Stream` 中增加了新的方法 `ofNullable`、`dropWhile`、`takeWhile` 和 `iterate` 方法。`Collectors` 中增加了新的方法 `filtering` 和 `flatMapping` -- `Optional` 类中新增了 `ifPresentOrElse`、`or` 和 `stream` 等方法 - -### 进程 API - -Java 9 增加了 `ProcessHandle` 接口,可以对原生进程进行管理,尤其适合于管理长时间运行的进程 - -### 平台日志 API 和服务 - -Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.LoggerFinder` 用来管理 JDK 使 用的日志记录器实现。JVM 在运行时只有一个系统范围的 `LoggerFinder` 实例。 - -我们可以通过添加自己的 `System.LoggerFinder` 实现来让 JDK 和应用使用 SLF4J 等其他日志记录框架。 - -### 反应式流 ( Reactive Streams ) - -- 在 Java9 中的 `java.util.concurrent.Flow` 类中新增了反应式流规范的核心接口 -- Flow 中包含了 `Flow.Publisher`、`Flow.Subscriber`、`Flow.Subscription` 和 `Flow.Processor` 等 4 个核心接口。Java 9 还提供了`SubmissionPublisher` 作为`Flow.Publisher` 的一个实现。 - -### 变量句柄 - -- 变量句柄是一个变量或一组变量的引用,包括静态域,非静态域,数组元素和堆外数据结构中的组成部分等 -- 变量句柄的含义类似于已有的方法句柄`MethodHandle` -- 由 Java 类`java.lang.invoke.VarHandle` 来表示,可以使用类 `java.lang.invoke.MethodHandles.Lookup` 中的静态工厂方法来创建 `VarHandle` 对 象 - -### 改进方法句柄(Method Handle) - -- 方法句柄从 Java7 开始引入,Java9 在类`java.lang.invoke.MethodHandles` 中新增了更多的静态方法来创建不同类型的方法句柄 - -### 其它新特性 - -- **接口私有方法** :Java 9 允许在接口中使用私有方法 -- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章 [https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html](https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html)) -- **类 `CompletableFuture` 中增加了几个新的方法(`completeAsync` ,`orTimeout` 等)** -- **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性 -- **I/O 流的新特性** :增加了新的方法来读取和复制 InputStream 中包含的数据 -- **改进应用的安全性能** :Java 9 新增了 4 个 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512 -- ...... - -## Java10 - -发布于 2018 年 3 月 20 日,最知名的特性应该是 var 关键字(局部变量类型推断)的引入了,其他还有垃圾收集器改善、GC 改进、性能提升、线程管控等一批新特性 - -### var 关键字 - -- **介绍** :提供了 var 关键字声明局部变量:`var list = new ArrayList(); // ArrayList` -- **局限性** :只能用于带有构造器的**局部变量**和 for 循环中 - -_Guide 哥:实际上 Lombok 早就体用了一个类似的关键字,使用它可以简化代码,但是可能会降低程序的易读性、可维护性。一般情况下,我个人都不太推荐使用。_ - -### 不可变集合 - -**list,set,map 提供了静态方法`copyOf()`返回入参集合的一个不可变拷贝(以下为 JDK 的源码)** - -```java -static List copyOf(Collection coll) { - return ImmutableCollections.listCopy(coll); -} -``` - -**`java.util.stream.Collectors`中新增了静态方法,用于将流中的元素收集为不可变的集合** - -### Optional - -- 新增了`orElseThrow()`方法来在没有值时抛出异常 - -### 并行全垃圾回收器 G1 - -从 Java9 开始 G1 就了默认的垃圾回收器,G1 是以一种低延时的垃圾回收器来设计的,旨在避免进行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用单线程去完成标记清除算法,这可能会导致垃圾回收期在无法回收内存的时候触发 Full GC。 - -为了最大限度地减少 Full GC 造成的应用停顿的影响,从 Java10 开始,G1 的 FullGC 改为并行的标记清除算法,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了 Full GC 的发生,以带来更好的性能提升、更大的吞吐量。 - -### 应用程序类数据共享 - -在 Java 5 中就已经引入了类数据共享机制 (Class Data Sharing,简称 CDS),允许将一组类预处理为共享归档文件,以便在运行时能够进行内存映射以减少 Java 程序的启动时间,当多个 Java 虚拟机(JVM)共享相同的归档文件时,还可以减少动态内存的占用量,同时减少多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用 - -Java 10 在现有的 CDS 功能基础上再次拓展,以允许应用类放置在共享存档中。CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS (Application Class-Data Sharing) 支持。其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升 - -### 其他特性 - -- **线程-局部管控**:Java 10 中线程管控引入 JVM 安全点的概念,将允许在不运行全局 JVM 安全点的情况下实现线程回调,由线程本身或者 JVM 线程来执行,同时保持线程处于阻塞状态,这种方式使得停止单个线程变成可能,而不是只能启用或停止所有线程 - -- **备用存储装置上的堆分配**:Java 10 中将使得 JVM 能够使用适用于不同类型的存储机制的堆,在可选内存设备上进行堆内存分配 -- **统一的垃圾回收接口**:Java 10 中,hotspot/gc 代码实现方面,引入一个干净的 GC 接口,改进不同 GC 源代码的隔离性,多个 GC 之间共享的实现细节代码应该存在于辅助类中。统一垃圾回收接口的主要原因是:让垃圾回收器(GC)这部分代码更加整洁,便于新人上手开发,便于后续排查相关问题。 - -## Java11 - -Java11 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本!Java 11 和 2017 年 9 月份发布的 Java 9 以及 2018 年 3 月份发布的 Java 10 相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,**Oracle 表示会对 Java 11 提供大力支持,这一支持将会持续至 2026 年 9 月。这是据 Java 8 以后支持的首个长期版本。** - -![](https://img-blog.csdnimg.cn/20210603202746605.png) - -### 字符串加强 - -Java 11 增加了一系列的字符串处理方法,如以下所示。 - -_Guide 哥:说白点就是多了层封装,JDK 开发组的人没少看市面上常见的工具类框架啊!_ - -```java -//判断字符串是否为空 -" ".isBlank();//true -//去除字符串首尾空格 -" Java ".strip();// "Java" -//去除字符串首部空格 -" Java ".stripLeading(); // "Java " -//去除字符串尾部空格 -" Java ".stripTrailing(); // " Java" -//重复字符串多少次 -"Java".repeat(3); // "JavaJavaJava" - -//返回由行终止符分隔的字符串集合。 -"A\nB\nC".lines().count(); // 3 -"A\nB\nC".lines().collect(Collectors.toList()); -``` - -### ZGC:可伸缩低延迟垃圾收集器 - -**ZGC 即 Z Garbage Collector**,是一个可伸缩的、低延迟的垃圾收集器。 - -ZGC 主要为了满足如下目标进行设计: - -- GC 停顿时间不超过 10ms -- 即能处理几百 MB 的小堆,也能处理几个 TB 的大堆 -- 应用吞吐能力不会下降超过 15%(与 G1 回收算法相比) -- 方便在此基础上引入新的 GC 特性和利用 colord -- 针以及 Load barriers 优化奠定基础 -- 当前只支持 Linux/x64 位平台 - -ZGC 目前 **处在实验阶段**,只支持 Linux/x64 平台 - -### 标准 HTTP Client 升级 - -Java 11 对 Java 9 中引入并在 Java 10 中进行了更新的 Http Client API 进行了标准化,在前两个版本中进行孵化的同时,Http Client 几乎被完全重写,并且现在完全支持异步非阻塞。 - -并且,Java11 中,Http Client 的包名由 `jdk.incubator.http` 改为`java.net.http`,该 API 通过 `CompleteableFuture` 提供非阻塞请求和响应语义。 - -使用起来也很简单,如下: - -```java -var request = HttpRequest.newBuilder() - - .uri(URI.create("https://javastack.cn")) - - .GET() - - .build(); - -var client = HttpClient.newHttpClient(); - -// 同步 - -HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - -System.out.println(response.body()); - -// 异步 - -client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) - - .thenApply(HttpResponse::body) - - .thenAccept(System.out::println); - - -``` - -### 简化启动单个源代码文件的方法 - -- 增强了 Java 启动器,使其能够运行单一文件的 Java 源代码。此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行。唯一的约束在于所有相关的类必须定义在同一个 Java 文件中 -- 对于 Java 初学者并希望尝试简单程序的人特别有用,并且能和 jshell 一起使用 -- 一定能程度上增强了使用 Java 来写脚本程序的能力 - -### 用于 Lambda 参数的局部变量语法 - -- 从 Java 10 开始,便引入了局部变量类型推断这一关键特性。类型推断允许使用关键字 var 作为局部变量的类型而不是实际类型,编译器根据分配给变量的值推断出类型 -- Java 10 中对 var 关键字存在几个限制 - - 只能用于局部变量上 - - 声明时必须初始化 - - 不能用作方法参数 - - 不能在 Lambda 表达式中使用 -- Java11 开始允许开发者在 Lambda 表达式中使用 var 进行参数声明 - -### 其他特性 - -- 新的垃圾回收器 Epsilon,一个完全消极的 GC 实现,分配有限的内存资源,最大限度的降低内存占用和内存吞吐延迟时间 -- 低开销的 Heap Profiling:Java 11 中提供一种低开销的 Java 堆分配采样方法,能够得到堆分配的 Java 对象信息,并且能够通过 JVMTI 访问堆信息 -- TLS1.3 协议:Java 11 中包含了传输层安全性(TLS)1.3 规范(RFC 8446)的实现,替换了之前版本中包含的 TLS,包括 TLS 1.2,同时还改进了其他 TLS 功能,例如 OCSP 装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627),在安全性和性能方面也做了很多提升 -- 飞行记录器:飞行记录器之前是商业版 JDK 的一项分析工具,但在 Java 11 中,其代码被包含到公开代码库中,这样所有人都能使用该功能了 - -## Java12 - -### 增强 Switch - -- 传统的 switch 语法存在容易漏写 break 的问题,而且从代码整洁性层面来看,多个 break 本质也是一种重复 - -- Java12 提供了 swtich 表达式,使用类似 lambda 语法条件匹配成功后的执行块,不需要多写 break - -- 作为预览特性加入,需要在`javac`编译和`java`运行时增加参数`--enable-preview` - - ```java - switch (day) { - case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); - case TUESDAY -> System.out.println(7); - case THURSDAY, SATURDAY -> System.out.println(8); - case WEDNESDAY -> System.out.println(9); - } - ``` - -### 数字格式化工具类 - -- `NumberFormat` 新增了对复杂的数字进行格式化的支持 - - ```java - NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); - String result = fmt.format(1000); - System.out.println(result); // 输出为 1K,计算工资是多少K更方便了。。。 - ``` - -### Shenandoah GC - -- Redhat 主导开发的 Pauseless GC 实现,主要目标是 99.9% 的暂停小于 10ms,暂停与堆大小无关等 -- 和 Java11 开源的 ZGC 相比(需要升级到 JDK11 才能使用),Shenandoah GC 有稳定的 JDK8u 版本,在 Java8 占据主要市场份额的今天有更大的可落地性 - -### G1 收集器提升 - -- **Java12 为默认的垃圾收集器 G1 带来了两项更新:** - - 可中止的混合收集集合:JEP344 的实现,为了达到用户提供的停顿时间目标,JEP 344 通过把要被回收的区域集(混合收集集合)拆分为强制和可选部分,使 G1 垃圾回收器能中止垃圾回收过程。 G1 可以中止可选部分的回收以达到停顿时间目标 - - 及时返回未使用的已分配内存:JEP346 的实现,增强 G1 GC,以便在空闲时自动将 Java 堆内存返回给操作系统 - -## Java13 - -### 引入 yield 关键字到 Switch 中 - -- `Switch` 表达式中就多了一个关键字用于跳出 `Switch` 块的关键字 `yield`,主要用于返回一个值 - -- `yield`和 `return` 的区别在于:`return` 会直接跳出当前循环或者方法,而 `yield` 只会跳出当前 `Switch` 块,同时在使用 `yield` 时,需要有 `default` 条件 - - ```java - private static String descLanguage(String name) { - return switch (name) { - case "Java": yield "object-oriented, platform independent and secured"; - case "Ruby": yield "a programmer's best friend"; - default: yield name +" is a good language"; - }; - } - ``` - -### 文本块 - -- 解决 Java 定义多行字符串时只能通过换行转义或者换行连接符来变通支持的问题,引入**三重双引号**来定义多行文本 - -- 两个`"""`中间的任何内容都会被解释为字符串的一部分,包括换行符 - - ```java - String json ="{\n" + - " \"name\":\"mkyong\",\n" + - " \"age\":38\n" + - "}\n"; // 未支持文本块之前 - ``` - - ```java - - String json = """ - { - "name":"mkyong", - "age":38 - } - """; - ``` - -### 增强 ZGC 释放未使用内存 - -- 在 Java 11 中是实验性的引入的 ZGC 在实际的使用中存在未能主动将未使用的内存释放给操作系统的问题 -- ZGC 堆由一组称为 ZPages 的堆区域组成。在 GC 周期中清空 ZPages 区域时,它们将被释放并返回到页面缓存 **ZPageCache** 中,此缓存中的 ZPages 按最近最少使用(LRU)的顺序,并按照大小进行组织 -- 在 Java 13 中,ZGC 将向操作系统返回被标识为长时间未使用的页面,这样它们将可以被其他进程重用 - -### SocketAPI 重构 - -- Java 13 为 Socket API 带来了新的底层实现方法,并且在 Java 13 中是默认使用新的 Socket 实现,使其易于发现并在排除问题同时增加可维护性 - -### 动态应用程序类-数据共享 - -- Java 13 中对 Java 10 中引入的 应用程序类数据共享进行了进一步的简化、改进和扩展,即:**允许在 Java 应用程序执行结束时动态进行类归档**,具体能够被归档的类包括:所有已被加载,但不属于默认基层 CDS 的应用程序类和引用类库中的类 - -## Java14 - -### record 关键字 - -- 简化数据类的定义方式,使用 record 代替 class 定义的类,只需要声明属性,就可以在获得属性的访问方法,以及 toString,hashCode,equals 方法 - -- 类似于使用 Class 定义类,同时使用了 lomobok 插件,并打上了`@Getter,@ToString,@EqualsAndHashCode`注解 - -- 作为预览特性引入 - - ```java - /** - * 这个类具有两个特征 - * 1. 所有成员属性都是final - * 2. 全部方法由构造方法,和两个成员属性访问器组成(共三个) - * 那么这种类就很适合使用record来声明 - */ - final class Rectangle implements Shape { - final double length; - final double width; - - public Rectangle(double length, double width) { - this.length = length; - this.width = width; - } - - double length() { return length; } - double width() { return width; } - } - /** - * 1. 使用record声明的类会自动拥有上面类中的三个方法 - * 2. 在这基础上还附赠了equals(),hashCode()方法以及toString()方法 - * 3. toString方法中包括所有成员属性的字符串表示形式及其名称 - */ - record Rectangle(float length, float width) { } - ``` - -### 空指针异常精准提示 - -- 通过 JVM 参数中添加`-XX:+ShowCodeDetailsInExceptionMessages`,可以在空指针异常中获取更为详细的调用信息,更快的定位和解决问题 - - ```java - a.b.c.i = 99; // 假设这段代码会发生空指针 - ``` - - ```java - Exception in thread "main" java.lang.NullPointerException: - Cannot read field 'c' because 'a.b' is null. - at Prog.main(Prog.java:5) // 增加参数后提示的异常中很明确的告知了哪里为空导致 - ``` - -### switch 的增强终于转正 - -- JDK12 引入的 switch(预览特性)在 JDK14 变为正式版本,不需要增加参数来启用,直接在 JDK14 中就能使用 -- 主要是用`->`来替代以前的`:`+`break`;另外就是提供了 yield 来在 block 中返回值 - -_Before Java 14_ - -```java -switch (day) { - case MONDAY: - case FRIDAY: - case SUNDAY: - System.out.println(6); - break; - case TUESDAY: - System.out.println(7); - break; - case THURSDAY: - case SATURDAY: - System.out.println(8); - break; - case WEDNESDAY: - System.out.println(9); - break; -} -``` - -_Java 14 enhancements_ - -```java -switch (day) { - case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); - case TUESDAY -> System.out.println(7); - case THURSDAY, SATURDAY -> System.out.println(8); - case WEDNESDAY -> System.out.println(9); -} -``` - -### instanceof 增强 - -- instanceof 主要在**类型强转前探测对象的具体类型**,然后执行具体的强转 - -- 新版的 instanceof 可以在判断的是否属于具体的类型同时完成转换 - -```java -Object obj = "我是字符串"; -if(obj instanceof String str){ - System.out.println(str); -} -``` - -### 其他特性 - -- 从 Java11 引入的 ZGC 作为继 G1 过后的下一代 GC 算法,从支持 Linux 平台到 Java14 开始支持 MacOS 和 Window(个人感觉是终于可以在日常开发工具中先体验下 ZGC 的效果了,虽然其实 G1 也够用) -- 移除了 CMS 垃圾收集器(功成而退) -- 新增了 jpackage 工具,标配将应用打成 jar 包外,还支持不同平台的特性包,比如 linux 下的`deb`和`rpm`,window 平台下的`msi`和`exe` - -## 总结 - -### 关于预览特性 - -- 先贴一段 oracle 官网原文:`This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future JDK releases. To compile and run code that contains preview features, you must specify additional command-line options.` -- 这是一个预览功能,该功能的设计,规格和实现是完整的,但不是永久性的,这意味着该功能可能以其他形式存在或在将来的 JDK 版本中根本不存在。 要编译和运行包含预览功能的代码,必须指定其他命令行选项。 -- 就以`switch`的增强为例子,从 Java12 中推出,到 Java13 中将继续增强,直到 Java14 才正式转正进入 JDK 可以放心使用,不用考虑后续 JDK 版本对其的改动或修改 -- 一方面可以看出 JDK 作为标准平台在增加新特性的严谨态度,另一方面个人认为是对于预览特性应该采取审慎使用的态度。特性的设计和实现容易,但是其实际价值依然需要在使用中去验证 - -### JVM 虚拟机优化 - -- 每次 Java 版本的发布都伴随着对 JVM 虚拟机的优化,包括对现有垃圾回收算法的改进,引入新的垃圾回收算法,移除老旧的不再适用于今天的垃圾回收算法等 -- 整体优化的方向是**高效,低时延的垃圾回收表现** -- 对于日常的应用开发者可能比较关注新的语法特性,但是从一个公司角度来说,在考虑是否升级 Java 平台时更加考虑的是**JVM 运行时的提升** - -## 参考信息 - -- IBM Developer Java9 -- Guide to Java10 -- Java 10 新特性介绍 -- IBM Devloper Java11 -- Java 11 – Features and Comparison: -- Oracle Java12 ReleaseNote -- Oracle Java13 ReleaseNote -- New Java13 Features -- Java13 新特性概述 -- Oracle Java14 record -- java14-features \ No newline at end of file diff --git "a/docs/java/JAD\345\217\215\347\274\226\350\257\221tricks.md" "b/docs/java/tips/JAD\345\217\215\347\274\226\350\257\221tricks.md" similarity index 68% rename from "docs/java/JAD\345\217\215\347\274\226\350\257\221tricks.md" rename to "docs/java/tips/JAD\345\217\215\347\274\226\350\257\221tricks.md" index f9d11bec9a9..34477a4591b 100644 --- "a/docs/java/JAD\345\217\215\347\274\226\350\257\221tricks.md" +++ "b/docs/java/tips/JAD\345\217\215\347\274\226\350\257\221tricks.md" @@ -1,10 +1,10 @@ -[jad](https://varaneckas.com/jad/)反编译工具,已经不再更新,且只支持JDK1.4,但并不影响其强大的功能。 +[jad](https://varaneckas.com/jad/)反编译工具,已经不再更新,且只支持 JDK1.4,但并不影响其强大的功能。 -基本用法:`jad xxx.class`,会生成直接可读的xxx.jad文件。 +基本用法:`jad xxx.class`,会生成直接可读的 `xxx.jad` 文件。 ## 自动拆装箱 -对于基本类型和包装类型之间的转换,通过xxxValue()和valueOf()两个方法完成自动拆装箱,使用jad进行反编译可以看到该过程: +对于基本类型和包装类型之间的转换,通过 xxxValue()和 valueOf()两个方法完成自动拆装箱,使用 jad 进行反编译可以看到该过程: ```java public class Demo { @@ -12,8 +12,9 @@ public class Demo { int x = new Integer(10); // 自动拆箱 Integer y = x; // 自动装箱 } -} +} ``` + 反编译后结果: ```java @@ -29,11 +30,9 @@ public class Demo } ``` +## foreach 语法糖 - -## foreach语法糖 - -在遍历迭代时可以foreach语法糖,对于数组类型直接转换成for循环: +在遍历迭代时可以 foreach 语法糖,对于数组类型直接转换成 for 循环: ```java // 原始代码 @@ -57,9 +56,7 @@ for(int j = 0; j < i; j++) } ``` - - -对于容器类的遍历会使用iterator进行迭代: +对于容器类的遍历会使用 iterator 进行迭代: ```java import java.io.PrintStream; @@ -85,11 +82,9 @@ public class Demo } ``` - - ## Arrays.asList(T...) -熟悉Arrays.asList(T...)用法的小伙伴都应该知道,asList()方法传入的参数不能是基本类型的数组,必须包装成包装类型再使用,否则对应生成的列表的大小永远是1: +熟悉 Arrays.asList(T...)用法的小伙伴都应该知道,asList()方法传入的参数不能是基本类型的数组,必须包装成包装类型再使用,否则对应生成的列表的大小永远是 1: ```java import java.util.*; @@ -105,7 +100,7 @@ public class Demo { } ``` -从反编译结果来解释,为什么传入基本类型的数组后,返回的List大小是1: +从反编译结果来解释,为什么传入基本类型的数组后,返回的 List 大小是 1: ```java // 反编译后文件 @@ -126,7 +121,7 @@ public class Demo Integer ainteger[] = { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3) }; - + // 注意这里被反编译成二维数组,而且是一个1行三列的二维数组 // list.size()当然返回1 List list = Arrays.asList(new int[][] { ai }); @@ -137,13 +132,11 @@ public class Demo } ``` -从上面结果可以看到,传入基本类型的数组后,会被转换成一个二维数组,而且是**new int\[1]\[arr.length]**这样的数组,调用list.size()当然返回1。 - - +从上面结果可以看到,传入基本类型的数组后,会被转换成一个二维数组,而且是**new int\[1]\[arr.length]**这样的数组,调用 list.size()当然返回 1。 ## 注解 -Java中的类、接口、枚举、注解都可以看做是类类型。使用jad来看一下@interface被转换成什么: +Java 中的类、接口、枚举、注解都可以看做是类类型。使用 jad 来看一下@interface 被转换成什么: ```java import java.lang.annotation.Retention; @@ -155,10 +148,11 @@ public @interface Foo{ boolean bar(); } ``` + 查看反编译代码可以看出: -- 自定义的注解类Foo被转换成接口Foo,并且继承Annotation接口 -- 原来自定义接口中的value()和bar()被转换成抽象方法 +- 自定义的注解类 Foo 被转换成接口 Foo,并且继承 Annotation 接口 +- 原来自定义接口中的 value()和 bar()被转换成抽象方法 ```java import java.lang.annotation.Annotation; @@ -171,7 +165,9 @@ public interface Foo public abstract boolean bar(); } ``` + 注解通常和反射配合使用,而且既然自定义的注解最终被转换成接口,注解中的属性被转换成接口中的抽象方法,那么通过反射之后拿到接口实例,在通过接口实例自然能够调用对应的抽象方法: + ```java import java.util.Arrays; @@ -185,12 +181,9 @@ public class Demo{ } ``` - ## 枚举 -通过jad反编译可以很好地理解枚举类。 - - +通过 jad 反编译可以很好地理解枚举类。 ### 空枚举 @@ -200,14 +193,15 @@ public class Demo{ public enum DummyEnum { } ``` -使用jad反编译查看结果: -- 自定义枚举类被转换成final类,并且继承Enum -- 提供了两个参数(name,odinal)的私有构造器,并且调用了父类的构造器。注意即使没有提供任何参数,也会有该该构造器,其中name就是枚举实例的名称,odinal是枚举实例的索引号 -- 初始化了一个private static final自定义类型的空数组 **$VALUES** -- 提供了两个public static方法: - - values()方法通过clone()方法返回内部$VALUES的浅拷贝。这个方法结合私有构造器可以完美实现单例模式,想一想values()方法是不是和单例模式中getInstance()方法功能类似 - - valueOf(String s):调用父类Enum的valueOf方法并强转返回 +使用 jad 反编译查看结果: + +- 自定义枚举类被转换成 final 类,并且继承 Enum +- 提供了两个参数(name,odinal)的私有构造器,并且调用了父类的构造器。注意即使没有提供任何参数,也会有该该构造器,其中 name 就是枚举实例的名称,odinal 是枚举实例的索引号 +- 初始化了一个 private static final 自定义类型的空数组 **\$VALUES** +- 提供了两个 public static 方法: + - values()方法通过 clone()方法返回内部\$VALUES 的浅拷贝。这个方法结合私有构造器可以完美实现单例模式,想一想 values()方法是不是和单例模式中 getInstance()方法功能类似 + - valueOf(String s):调用父类 Enum 的 valueOf 方法并强转返回 ```java public final class DummyEnum extends Enum @@ -233,6 +227,7 @@ public final class DummyEnum extends Enum } ``` + ### 包含抽象方法的枚举 枚举类中也可以包含抽象方法,但是必须定义枚举实例并且立即重写抽象方法,就像下面这样: @@ -255,22 +250,23 @@ public enum DummyEnum { } ``` + 再来反编译看看有哪些变化: -- 原来final class变成了abstract class:这很好理解,有抽象方法的类自然是抽象类 -- 多了两个public static final的成员DUMMY1、DUMMY2,这两个实例的初始化过程被放到了static代码块中,并且实例过程中直接重写了抽象方法,类似于匿名内部类的形式。 -- 数组**$VALUES[]**初始化时放入枚举实例 +- 原来 final class 变成了 abstract class:这很好理解,有抽象方法的类自然是抽象类 +- 多了两个 public static final 的成员 DUMMY1、DUMMY2,这两个实例的初始化过程被放到了 static 代码块中,并且实例过程中直接重写了抽象方法,类似于匿名内部类的形式。 +- 数组**\$VALUES[]**初始化时放入枚举实例 还有其它变化么? -在反编译后的DummyEnum类中,是存在抽象方法的,而枚举实例在静态代码块中初始化过程中重写了抽象方法。在Java中,抽象方法和抽象方法重写同时放在一个类中,只能通过内部类形式完成。因此上面第二点应该说成就是以内部类形式初始化。 +在反编译后的 DummyEnum 类中,是存在抽象方法的,而枚举实例在静态代码块中初始化过程中重写了抽象方法。在 Java 中,抽象方法和抽象方法重写同时放在一个类中,只能通过内部类形式完成。因此上面第二点应该说成就是以内部类形式初始化。 -可以看一下DummyEnum.class存放的位置,应该多了两个文件: +可以看一下 DummyEnum.class 存放的位置,应该多了两个文件: -- DummyEnum$1.class -- DummyEnum$2.class +- DummyEnum\$1.class +- DummyEnum\$2.class -Java中.class文件出现$符号表示有内部类存在,就像OutClass$InnerClass,这两个文件出现也应证了上面的匿名内部类初始化的说法。 +Java 中.class 文件出现 $ 符号表示有内部类存在,就像OutClass$InnerClass,这两个文件出现也应证了上面的匿名内部类初始化的说法。 ```java import java.io.PrintStream; @@ -299,9 +295,9 @@ public abstract class DummyEnum extends Enum public static final DummyEnum DUMMY1; public static final DummyEnum DUMMY2; private static final DummyEnum $VALUES[]; - + // static代码块进行初始化 - static + static { DUMMY1 = new DummyEnum("DUMMY1", 0) { public void dummyMethod() @@ -325,13 +321,11 @@ public abstract class DummyEnum extends Enum } ``` - - ### 正常的枚举类 -实际开发中,枚举类通常的形式是有两个参数(int code,Sring msg)的构造器,可以作为状态码进行返回。Enum类实际上也是提供了包含两个参数且是protected的构造器,这里为了避免歧义,将枚举类的构造器设置为三个,使用jad反编译: +实际开发中,枚举类通常的形式是有两个参数(int code,Sring msg)的构造器,可以作为状态码进行返回。Enum 类实际上也是提供了包含两个参数且是 protected 的构造器,这里为了避免歧义,将枚举类的构造器设置为三个,使用 jad 反编译: -最大的变化是:现在的private构造器从2个参数变成5个,而且在内部仍然将前两个参数通过super传递给父类,剩余的三个参数才是真正自己提供的参数。可以想象,如果自定义的枚举类只提供了一个参数,最终生成底层代码中private构造器应该有三个参数,前两个依然通过super传递给父类。 +最大的变化是:现在的 private 构造器从 2 个参数变成 5 个,而且在内部仍然将前两个参数通过 super 传递给父类,剩余的三个参数才是真正自己提供的参数。可以想象,如果自定义的枚举类只提供了一个参数,最终生成底层代码中 private 构造器应该有三个参数,前两个依然通过 super 传递给父类。 ```java public final class CustomEnum extends Enum @@ -362,7 +356,7 @@ public final class CustomEnum extends Enum private Object data; private static final CustomEnum $VALUES[]; - static + static { FIRST = new CustomEnum("FIRST", 0, 10010, "first", Long.valueOf(100L)); SECOND = new CustomEnum("SECOND", 1, 10020, "second", "Foo"); diff --git "a/docs/java/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" "b/docs/java/tips/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" similarity index 54% rename from "docs/java/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" rename to "docs/java/tips/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" index 980eeefb2b1..2550d8b38dd 100644 --- "a/docs/java/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" +++ "b/docs/java/tips/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\345\256\232\344\275\215\345\270\270\350\247\201Java\346\200\247\350\203\275\351\227\256\351\242\230.md" @@ -1,27 +1,27 @@ -## 手把手教你定位常见Java性能问题 +## 手把手教你定位常见 Java 性能问题 ## 概述 -性能优化一向是后端服务优化的重点,但是线上性能故障问题不是经常出现,或者受限于业务产品,根本就没办法出现性能问题,包括笔者自己遇到的性能问题也不多,所以为了提前储备知识,当出现问题的时候不会手忙脚乱,我们本篇文章来模拟下常见的几个Java性能故障,来学习怎么去分析和定位。 +性能优化一向是后端服务优化的重点,但是线上性能故障问题不是经常出现,或者受限于业务产品,根本就没办法出现性能问题,包括笔者自己遇到的性能问题也不多,所以为了提前储备知识,当出现问题的时候不会手忙脚乱,我们本篇文章来模拟下常见的几个 Java 性能故障,来学习怎么去分析和定位。 ## 预备知识 既然是定位问题,肯定是需要借助工具,我们先了解下需要哪些工具可以帮忙定位问题。 - **top命令** +**top 命令** -`top`命令使我们最常用的Linux命令之一,它可以实时的显示当前正在执行的进程的CPU使用率,内存使用率等系统信息。`top -Hp pid` 可以查看线程的系统资源使用情况。 +`top`命令使我们最常用的 Linux 命令之一,它可以实时的显示当前正在执行的进程的 CPU 使用率,内存使用率等系统信息。`top -Hp pid` 可以查看线程的系统资源使用情况。 - **vmstat命令** +**vmstat 命令** -vmstat是一个指定周期和采集次数的虚拟内存检测工具,可以统计内存,CPU,swap的使用情况,它还有一个重要的常用功能,用来观察进程的上下文切换。字段说明如下: +vmstat 是一个指定周期和采集次数的虚拟内存检测工具,可以统计内存,CPU,swap 的使用情况,它还有一个重要的常用功能,用来观察进程的上下文切换。字段说明如下: -- r: 运行队列中进程数量(当数量大于CPU核数表示有阻塞的线程) -- b: 等待IO的进程数量 +- r: 运行队列中进程数量(当数量大于 CPU 核数表示有阻塞的线程) +- b: 等待 IO 的进程数量 - swpd: 使用虚拟内存大小 - free: 空闲物理内存大小 - buff: 用作缓冲的内存大小(内存和硬盘的缓冲区) -- cache: 用作缓存的内存大小(CPU和内存之间的缓冲区) +- cache: 用作缓存的内存大小(CPU 和内存之间的缓冲区) - si: 每秒从交换区写到内存的大小,由磁盘调入内存 - so: 每秒写入交换区的内存大小,由内存调入磁盘 - bi: 每秒读取的块数 @@ -30,30 +30,30 @@ vmstat是一个指定周期和采集次数的虚拟内存检测工具,可以 - cs: 每秒上下文切换数。 - us: 用户进程执行时间百分比(user time) - sy: 内核系统进程执行时间百分比(system time) -- wa: IO等待时间百分比 +- wa: IO 等待时间百分比 - id: 空闲时间百分比 - **pidstat命令** + **pidstat 命令** pidstat 是 Sysstat 中的一个组件,也是一款功能强大的性能监测工具,`top` 和 `vmstat` 两个命令都是监测进程的内存、CPU 以及 I/O 使用情况,而 pidstat 命令可以检测到线程级别的。`pidstat`命令线程切换字段说明如下: -- UID :被监控任务的真实用户ID。 +- UID :被监控任务的真实用户 ID。 -- TGID :线程组ID。 +- TGID :线程组 ID。 -- TID:线程ID。 +- TID:线程 ID。 - cswch/s:主动切换上下文次数,这里是因为资源阻塞而切换线程,比如锁等待等情况。 -- nvcswch/s:被动切换上下文次数,这里指CPU调度切换了线程。 +- nvcswch/s:被动切换上下文次数,这里指 CPU 调度切换了线程。 - **jstack命令** + **jstack 命令** -jstack是JDK工具命令,它是一种线程堆栈分析工具,最常用的功能就是使用 `jstack pid` 命令查看线程的堆栈信息,也经常用来排除死锁情况。 +jstack 是 JDK 工具命令,它是一种线程堆栈分析工具,最常用的功能就是使用 `jstack pid` 命令查看线程的堆栈信息,也经常用来排除死锁情况。 **jstat 命令** -它可以检测Java程序运行的实时情况,包括堆内存信息和垃圾回收信息,我们常常用来查看程序垃圾回收情况。常用的命令是`jstat -gc pid`。信息字段说明如下: +它可以检测 Java 程序运行的实时情况,包括堆内存信息和垃圾回收信息,我们常常用来查看程序垃圾回收情况。常用的命令是`jstat -gc pid`。信息字段说明如下: - S0C:年轻代中 To Survivor 的容量(单位 KB); @@ -85,31 +85,27 @@ jstack是JDK工具命令,它是一种线程堆栈分析工具,最常用的 - GCT:从应用程序启动到采样时 gc 用的总时间 (s)。 + **jmap 命令** +jmap 也是 JDK 工具命令,他可以查看堆内存的初始化信息以及堆内存的使用情况,还可以生成 dump 文件来进行详细分析。查看堆内存情况命令`jmap -heap pid`。 - **jmap命令** +**mat 内存工具** -jmap也是JDK工具命令,他可以查看堆内存的初始化信息以及堆内存的使用情况,还可以生成dump文件来进行详细分析。查看堆内存情况命令`jmap -heap pid`。 +MAT(Memory Analyzer Tool)工具是 eclipse 的一个插件(MAT 也可以单独使用),它分析大内存的 dump 文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用 OQL 对象查询,以及可以很方便的找出对象 GC Roots 的相关信息。 - **mat内存工具** +**idea 中也有这么一个插件,就是 JProfiler**。 -MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使用),它分析大内存的dump文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用OQL对象查询,以及可以很方便的找出对象GC Roots的相关信息。 - -**idea中也有这么一个插件,就是JProfiler**。 - -相关阅读: - -1. 《性能诊断利器 JProfiler 快速入门和最佳实践》:[https://segmentfault.com/a/1190000017795841](https://segmentfault.com/a/1190000017795841) +相关阅读:[《性能诊断利器 JProfiler 快速入门和最佳实践》](https://segmentfault.com/a/1190000017795841) ## 模拟环境准备 -基础环境jdk1.8,采用SpringBoot框架来写几个接口来触发模拟场景,首先是模拟CPU占满情况 +基础环境 jdk1.8,采用 SpringBoot 框架来写几个接口来触发模拟场景,首先是模拟 CPU 占满情况 -## CPU占满 +### CPU 占满 -模拟CPU占满还是比较简单,直接写一个死循环计算消耗CPU即可。 +模拟 CPU 占满还是比较简单,直接写一个死循环计算消耗 CPU 即可。 -````java +```java /** * 模拟CPU占满 */ @@ -127,27 +123,27 @@ MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使 } } -```` +``` -请求接口地址测试`curl localhost:8080/cpu/loop`,发现CPU立马飙升到100% +请求接口地址测试`curl localhost:8080/cpu/loop`,发现 CPU 立马飙升到 100% ![](./images/performance-tuning/java-performance1.png) -通过执行`top -Hp 32805` 查看Java线程情况 +通过执行`top -Hp 32805` 查看 Java 线程情况 ![](./images/performance-tuning/java-performance2.png) -执行 `printf '%x' 32826` 获取16进制的线程id,用于`dump`信息查询,结果为 `803a`。最后我们执行`jstack 32805 |grep -A 20 803a `来查看下详细的`dump`信息。 +执行 `printf '%x' 32826` 获取 16 进制的线程 id,用于`dump`信息查询,结果为 `803a`。最后我们执行`jstack 32805 |grep -A 20 803a`来查看下详细的`dump`信息。 ![](./images/performance-tuning/java-performance3.png) -这里`dump`信息直接定位出了问题方法以及代码行,这就定位出了CPU占满的问题。 +这里`dump`信息直接定位出了问题方法以及代码行,这就定位出了 CPU 占满的问题。 -## 内存泄露 +### 内存泄露 -模拟内存泄漏借助了ThreadLocal对象来完成,ThreadLocal是一个线程私有变量,可以绑定到线程上,在整个线程的生命周期都会存在,但是由于ThreadLocal的特殊性,ThreadLocal是基于ThreadLocalMap实现的,ThreadLocalMap的Entry继承WeakReference,而Entry的Key是WeakReference的封装,换句话说Key就是弱引用,弱引用在下次GC之后就会被回收,如果ThreadLocal在set之后不进行后续的操作,因为GC会把Key清除掉,但是Value由于线程还在存活,所以Value一直不会被回收,最后就会发生内存泄漏。 +模拟内存泄漏借助了 ThreadLocal 对象来完成,ThreadLocal 是一个线程私有变量,可以绑定到线程上,在整个线程的生命周期都会存在,但是由于 ThreadLocal 的特殊性,ThreadLocal 是基于 ThreadLocalMap 实现的,ThreadLocalMap 的 Entry 继承 WeakReference,而 Entry 的 Key 是 WeakReference 的封装,换句话说 Key 就是弱引用,弱引用在下次 GC 之后就会被回收,如果 ThreadLocal 在 set 之后不进行后续的操作,因为 GC 会把 Key 清除掉,但是 Value 由于线程还在存活,所以 Value 一直不会被回收,最后就会发生内存泄漏。 -````Java +```Java /** * 模拟内存泄漏 */ @@ -158,43 +154,43 @@ MAT(Memory Analyzer Tool)工具是eclipse的一个插件(MAT也可以单独使 localVariable.set(new Byte[4096 * 1024]);// 为线程添加变量 return "ok"; } -```` +``` 我们给启动加上堆内存大小限制,同时设置内存溢出的时候输出堆栈快照并输出日志。 -`java -jar -Xms500m -Xmx500m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heaplog.log analysis-demo-0.0.1-SNAPSHOT.jar` +`java -jar -Xms500m -Xmx500m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heaplog.log analysis-demo-0.0.1-SNAPSHOT.jar` -启动成功后我们循环执行100次,`for i in {1..500}; do curl localhost:8080/memory/leak;done`,还没执行完毕,系统已经返回500错误了。查看系统日志出现了如下异常: +启动成功后我们循环执行 100 次,`for i in {1..500}; do curl localhost:8080/memory/leak;done`,还没执行完毕,系统已经返回 500 错误了。查看系统日志出现了如下异常: ``` java.lang.OutOfMemoryError: Java heap space ``` -我们用`jstat -gc pid` 命令来看看程序的GC情况。 +我们用`jstat -gc pid` 命令来看看程序的 GC 情况。 ![](./images/performance-tuning/java-performance4.png) -很明显,内存溢出了,堆内存经过45次 Full Gc 之后都没释放出可用内存,这说明当前堆内存中的对象都是存活的,有GC Roots引用,无法回收。那是什么原因导致内存溢出呢?是不是我只要加大内存就行了呢?如果是普通的内存溢出也许扩大内存就行了,但是如果是内存泄漏的话,扩大的内存不一会就会被占满,所以我们还需要确定是不是内存泄漏。我们之前保存了堆 Dump 文件,这个时候借助我们的MAT工具来分析下。导入工具选择`Leak Suspects Report`,工具直接就会给你列出问题报告。 +很明显,内存溢出了,堆内存经过 45 次 Full Gc 之后都没释放出可用内存,这说明当前堆内存中的对象都是存活的,有 GC Roots 引用,无法回收。那是什么原因导致内存溢出呢?是不是我只要加大内存就行了呢?如果是普通的内存溢出也许扩大内存就行了,但是如果是内存泄漏的话,扩大的内存不一会就会被占满,所以我们还需要确定是不是内存泄漏。我们之前保存了堆 Dump 文件,这个时候借助我们的 MAT 工具来分析下。导入工具选择`Leak Suspects Report`,工具直接就会给你列出问题报告。 ![](./images/performance-tuning/java-performance5.png) -这里已经列出了可疑的4个内存泄漏问题,我们点击其中一个查看详情。 +这里已经列出了可疑的 4 个内存泄漏问题,我们点击其中一个查看详情。 ![](./images/performance-tuning/java-performance6.png) -这里已经指出了内存被线程占用了接近50M的内存,占用的对象就是ThreadLocal。如果想详细的通过手动去分析的话,可以点击`Histogram`,查看最大的对象占用是谁,然后再分析它的引用关系,即可确定是谁导致的内存溢出。 +这里已经指出了内存被线程占用了接近 50M 的内存,占用的对象就是 ThreadLocal。如果想详细的通过手动去分析的话,可以点击`Histogram`,查看最大的对象占用是谁,然后再分析它的引用关系,即可确定是谁导致的内存溢出。 ![](./images/performance-tuning/java-performance7.png) -上图发现占用内存最大的对象是一个Byte数组,我们看看它到底被那个GC Root引用导致没有被回收。按照上图红框操作指引,结果如下图: +上图发现占用内存最大的对象是一个 Byte 数组,我们看看它到底被那个 GC Root 引用导致没有被回收。按照上图红框操作指引,结果如下图: ![](./images/performance-tuning/java-performance8.png) -我们发现Byte数组是被线程对象引用的,图中也标明,Byte数组对像的GC Root是线程,所以它是不会被回收的,展开详细信息查看,我们发现最终的内存占用对象是被ThreadLocal对象占据了。这也和MAT工具自动帮我们分析的结果一致。 +我们发现 Byte 数组是被线程对象引用的,图中也标明,Byte 数组对像的 GC Root 是线程,所以它是不会被回收的,展开详细信息查看,我们发现最终的内存占用对象是被 ThreadLocal 对象占据了。这也和 MAT 工具自动帮我们分析的结果一致。 -## 死锁 +### 死锁 -死锁会导致耗尽线程资源,占用内存,表现就是内存占用升高,CPU不一定会飙升(看场景决定),如果是直接new线程,会导致JVM内存被耗尽,报无法创建线程的错误,这也是体现了使用线程池的好处。 +死锁会导致耗尽线程资源,占用内存,表现就是内存占用升高,CPU 不一定会飙升(看场景决定),如果是直接 new 线程,会导致 JVM 内存被耗尽,报无法创建线程的错误,这也是体现了使用线程池的好处。 ```java ExecutorService service = new ThreadPoolExecutor(4, 10, @@ -240,13 +236,13 @@ public class DeadLockThread implements Runnable { } ``` -我们循环请求接口2000次,发现不一会系统就出现了日志错误,线程池和队列都满了,由于我选择的当队列满了就拒绝的策略,所以系统直接抛出异常。 +我们循环请求接口 2000 次,发现不一会系统就出现了日志错误,线程池和队列都满了,由于我选择的当队列满了就拒绝的策略,所以系统直接抛出异常。 ``` java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@2760298 rejected from java.util.concurrent.ThreadPoolExecutor@7ea7cd51[Running, pool size = 10, active threads = 10, queued tasks = 1024, completed tasks = 846] ``` -通过`ps -ef|grep java`命令找出 Java 进程 pid,执行`jstack pid` 即可出现java线程堆栈信息,这里发现了5个死锁,我们只列出其中一个,很明显线程`pool-1-thread-2`锁住了`0x00000000f8387d88`等待`0x00000000f8387d98`锁,线程`pool-1-thread-1`锁住了`0x00000000f8387d98`等待锁`0x00000000f8387d88`,这就产生了死锁。 +通过`ps -ef|grep java`命令找出 Java 进程 pid,执行`jstack pid` 即可出现 java 线程堆栈信息,这里发现了 5 个死锁,我们只列出其中一个,很明显线程`pool-1-thread-2`锁住了`0x00000000f8387d88`等待`0x00000000f8387d98`锁,线程`pool-1-thread-1`锁住了`0x00000000f8387d98`等待锁`0x00000000f8387d88`,这就产生了死锁。 ```JAVA Java stack information for the threads listed above: @@ -269,13 +265,13 @@ Java stack information for the threads listed above: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) - + Found 5 deadlocks. ``` -## 线程频繁切换 +### 线程频繁切换 -上下文切换会导致将大量CPU时间浪费在寄存器、内核栈以及虚拟内存的保存和恢复上,导致系统整体性能下降。当你发现系统的性能出现明显的下降时候,需要考虑是否发生了大量的线程上下文切换。 +上下文切换会导致将大量 CPU 时间浪费在寄存器、内核栈以及虚拟内存的保存和恢复上,导致系统整体性能下降。当你发现系统的性能出现明显的下降时候,需要考虑是否发生了大量的线程上下文切换。 ```java @GetMapping(value = "/thread/swap") @@ -303,7 +299,7 @@ public class ThreadSwap1 implements Runnable { } ``` -这里我创建多个线程去执行基础的原子+1操作,然后让出 CPU 资源,理论上 CPU 就会去调度别的线程,我们请求接口创建100个线程看看效果如何,`curl localhost:8080/thread/swap?num=100`。接口请求成功后,我们执行`vmstat 1 10,表示每1秒打印一次,打印10次,线程切换采集结果如下: +这里我创建多个线程去执行基础的原子+1 操作,然后让出 CPU 资源,理论上 CPU 就会去调度别的线程,我们请求接口创建 100 个线程看看效果如何,`curl localhost:8080/thread/swap?num=100`。接口请求成功后,我们执行 `vmstat 1 10`,表示每 1 秒打印一次,打印 10 次,线程切换采集结果如下: ``` procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- @@ -316,37 +312,35 @@ procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 100 0 128000 878384 908 468688 0 0 0 108 4182 8346826 14 86 0 0 0 ``` +这里我们关注 4 个指标,`r`,`cs`,`us`,`sy`。 +**r=100**,说明等待的进程数量是 100,线程有阻塞。 -这里我们关注4个指标,`r`,`cs`,`us`,`sy`。 - -**r=100**,说明等待的进程数量是100,线程有阻塞。 - -**cs=800多万**,说明每秒上下文切换了800多万次,这个数字相当大了。 +**cs=800 多万**,说明每秒上下文切换了 800 多万次,这个数字相当大了。 -**us=14**,说明用户态占用了14%的CPU时间片去处理逻辑。 +**us=14**,说明用户态占用了 14%的 CPU 时间片去处理逻辑。 -**sy=86**,说明内核态占用了86%的CPU,这里明显就是做上下文切换工作了。 +**sy=86**,说明内核态占用了 86%的 CPU,这里明显就是做上下文切换工作了。 -我们通过`top`命令以及`top -Hp pid`查看进程和线程CPU情况,发现Java线程CPU占满了,但是线程CPU使用情况很平均,没有某一个线程把CPU吃满的情况。 +我们通过`top`命令以及`top -Hp pid`查看进程和线程 CPU 情况,发现 Java 线程 CPU 占满了,但是线程 CPU 使用情况很平均,没有某一个线程把 CPU 吃满的情况。 ``` -PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 87093 root 20 0 4194788 299056 13252 S 399.7 16.1 65:34.67 java +PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 87093 root 20 0 4194788 299056 13252 S 399.7 16.1 65:34.67 java ``` ``` - PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND - 87189 root 20 0 4194788 299056 13252 R 4.7 16.1 0:41.11 java - 87129 root 20 0 4194788 299056 13252 R 4.3 16.1 0:41.14 java - 87130 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.51 java - 87133 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.59 java - 87134 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.95 java + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 87189 root 20 0 4194788 299056 13252 R 4.7 16.1 0:41.11 java + 87129 root 20 0 4194788 299056 13252 R 4.3 16.1 0:41.14 java + 87130 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.51 java + 87133 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.59 java + 87134 root 20 0 4194788 299056 13252 R 4.3 16.1 0:40.95 java ``` -结合上面用户态CPU只使用了14%,内核态CPU占用了86%,可以基本判断是Java程序线程上下文切换导致性能问题。 +结合上面用户态 CPU 只使用了 14%,内核态 CPU 占用了 86%,可以基本判断是 Java 程序线程上下文切换导致性能问题。 -我们使用`pidstat`命令来看看Java进程内部的线程切换数据,执行`pidstat -p 87093 -w 1 10 `,采集数据如下: +我们使用`pidstat`命令来看看 Java 进程内部的线程切换数据,执行`pidstat -p 87093 -w 1 10`,采集数据如下: ``` 11:04:30 PM UID TGID TID cswch/s nvcswch/s Command @@ -362,19 +356,19 @@ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 11:04:30 PM 0 - 87137 0.00 16.04 |__java ``` -根据上面采集的信息,我们知道Java的线程每秒切换15次左右,正常情况下,应该是个位数或者小数。结合这些信息我们可以断定Java线程开启过多,导致频繁上下文切换,从而影响了整体性能。 +根据上面采集的信息,我们知道 Java 的线程每秒切换 15 次左右,正常情况下,应该是个位数或者小数。结合这些信息我们可以断定 Java 线程开启过多,导致频繁上下文切换,从而影响了整体性能。 -**为什么系统的上下文切换是每秒800多万,而 Java 进程中的某一个线程切换才15次左右?** +**为什么系统的上下文切换是每秒 800 多万,而 Java 进程中的某一个线程切换才 15 次左右?** 系统上下文切换分为三种情况: -1、多任务:在多任务环境中,一个进程被切换出CPU,运行另外一个进程,这里会发生上下文切换。 +1、多任务:在多任务环境中,一个进程被切换出 CPU,运行另外一个进程,这里会发生上下文切换。 -2、中断处理:发生中断时,硬件会切换上下文。在vmstat命令中是`in` +2、中断处理:发生中断时,硬件会切换上下文。在 vmstat 命令中是`in` 3、用户和内核模式切换:当操作系统中需要在用户模式和内核模式之间进行转换时,需要进行上下文切换,比如进行系统函数调用。 -Linux 为每个 CPU 维护了一个就绪队列,将活跃进程按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。也就是vmstat命令中的`r`。 +Linux 为每个 CPU 维护了一个就绪队列,将活跃进程按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。也就是 vmstat 命令中的`r`。 那么,进程在什么时候才会被调度到 CPU 上运行呢? @@ -385,13 +379,13 @@ Linux 为每个 CPU 维护了一个就绪队列,将活跃进程按照优先级 - 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行。 - 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。 -结合我们之前的内容分析,阻塞的就绪队列是100左右,而我们的CPU只有4核,这部分原因造成的上下文切换就可能会相当高,再加上中断次数是4000左右和系统的函数调用等,整个系统的上下文切换到800万也不足为奇了。Java内部的线程切换才15次,是因为线程使用`Thread.yield()`来让出CPU资源,但是CPU有可能继续调度该线程,这个时候线程之间并没有切换,这也是为什么内部的某个线程切换次数并不是非常大的原因。 +结合我们之前的内容分析,阻塞的就绪队列是 100 左右,而我们的 CPU 只有 4 核,这部分原因造成的上下文切换就可能会相当高,再加上中断次数是 4000 左右和系统的函数调用等,整个系统的上下文切换到 800 万也不足为奇了。Java 内部的线程切换才 15 次,是因为线程使用`Thread.yield()`来让出 CPU 资源,但是 CPU 有可能继续调度该线程,这个时候线程之间并没有切换,这也是为什么内部的某个线程切换次数并不是非常大的原因。 ## 总结 -本文模拟了常见的性能问题场景,分析了如何定位CPU100%、内存泄漏、死锁、线程频繁切换问题。分析问题我们需要做好两件事,第一,掌握基本的原理,第二,借助好工具。本文也列举了分析问题的常用工具和命令,希望对你解决问题有所帮助。当然真正的线上环境可能十分复杂,并没有模拟的环境那么简单,但是原理是一样的,问题的表现也是类似的,我们重点抓住原理,活学活用,相信复杂的线上问题也可以顺利解决。 +本文模拟了常见的性能问题场景,分析了如何定位 CPU100%、内存泄漏、死锁、线程频繁切换问题。分析问题我们需要做好两件事,第一,掌握基本的原理,第二,借助好工具。本文也列举了分析问题的常用工具和命令,希望对你解决问题有所帮助。当然真正的线上环境可能十分复杂,并没有模拟的环境那么简单,但是原理是一样的,问题的表现也是类似的,我们重点抓住原理,活学活用,相信复杂的线上问题也可以顺利解决。 -## 参考 +## 参考 1、https://linux.die.net/man/1/pidstat diff --git a/docs/system-design/distributed-system/zookeeper/zookeeper-plus.md b/docs/system-design/distributed-system/zookeeper/zookeeper-plus.md index 44b5abf6f48..ef37abb3c75 100644 --- a/docs/system-design/distributed-system/zookeeper/zookeeper-plus.md +++ b/docs/system-design/distributed-system/zookeeper/zookeeper-plus.md @@ -184,7 +184,7 @@ ### 5.1. `Zookeeper` 架构 -作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Automic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复** 。 +作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Atomic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复** 。 ![Zookeeper架构](https://img-blog.csdnimg.cn/img_convert/0c38d08ea026e25bf3849cc7654a4e79.png) diff --git a/docs/system-design/naming.md b/docs/system-design/naming.md index 33d0db49286..33579c9160c 100644 --- a/docs/system-design/naming.md +++ b/docs/system-design/naming.md @@ -1,14 +1,42 @@ -编程过程中,有太多太多让我们头疼的事情了,比如命名、维护其他人的代码、写测试、与其他人沟通交流等等。就连世界级软件大师 **Martin Fowler** 大神都说过 CS 领域有两大最难的事情,一是**缓存失效**,一是**程序命名**(@ [https://martinfowler.com/bliki/TwoHardThings.html](https://martinfowler.com/bliki/TwoHardThings.html))。 +> 可选标题:工作半年,变量命名不规范,被diss了! +> +> 项目组新来的实习生因为变量命名被 “diss” 了! + +大家好,这里是热爱分享的 Guide ! + +我还记得我刚工作那一段时间, 项目 Code Review 的时候,我经常因为变量命名不规范而被 “diss”! + +究其原因还是自己那会经验不足,而且,大学那会写项目的时候不太注意这些问题,想着只要把功能实现出来就行了。 + +但是,工作中就不一样,为了代码的可读性、可维护性,项目组对于代码质量的要求还是很高的! + +前段时间,项目组新来的一个实习生也经常在 Code Review 因为变量命名不规范而被 “diss”,这让我想到自己刚到公司写代码那会的日子。 + +于是,我就简单写了这篇关于变量命名规范的文章,希望能对同样有此困扰的小伙伴提供一些帮助。 -![](pictures/marting-naming.png) +确实,编程过程中,有太多太多让我们头疼的事情了,比如命名、维护其他人的代码、写测试、与其他人沟通交流等等。 -今天 Guide 就单独拎出 “**命名**” 来聊聊,据说之前在 Quora 网站,由接近 5000 名程序员票选出来的最难的事情就是“命名”。 +据说之前在 Quora 网站,由接近 5000 名程序员票选出来的最难的事情就是“命名”。 + +大名鼎鼎的《重构》的作者老马(Martin Fowler)曾经在[TwoHardThings](https://martinfowler.com/bliki/TwoHardThings.html)这篇文章中提到过CS 领域有两大最难的事情:一是 **缓存失效** ,一是 **程序命名** 。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/marting-naming.png) + +这个句话实际上也是老马引用别人的,类似的表达还有很多。比如分布式系统领域有两大最难的事情:一是 **保证消息顺序** ,一是 **严格一次传递** 。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/20210629104844645.png) + +今天咱们就单独拎出 “**命名**” 来聊聊! 这篇文章配合我之前发的 [《编码 5 分钟,命名 2 小时?史上最全的 Java 命名规范参考!》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486449&idx=1&sn=c3b502529ff991c7180281bcc22877af&chksm=cea2443af9d5cd2c1c87049ed15ccf6f88275419c7dbe542406166a703b27d0f3ecf2af901f8&token=999884676&lang=zh_CN#rd) 这篇文章阅读效果更佳哦! ## 为什么需要重视命名? -**好的命名即是注释,别人一看到你的命名就知道你的变量、方法或者类是做什么的!** 好的命名对于其他人(包括你自己)理解你的代码有着很大的帮助! +咱们需要先搞懂为什么要重视编程中的命名这一行为,它对于我们的编码工作有着什么意义。 + +**为什么命名很重要呢?** 这是因为 **好的命名即是注释,别人一看到你的命名就知道你的变量、方法或者类是做什么的!** + +简单来说就是 **别人根据你的命名就能知道你的代码要表达的意思** (不过,前提这个人也要有基本的英语知识,对于一些编程中常见的单词比较熟悉)。 简单举个例子说明一下命名的重要性。 @@ -64,7 +92,9 @@ serviceDiscovery、Serviceinstance、LRUCacheFactory 正例: ```java -getUserInfo()、createCustomThreadPool()、setNameFormat(String nameFormat) +getUserInfo() +createCustomThreadPool() +setNameFormat(String nameFormat) Uservice userService; ``` @@ -81,7 +111,9 @@ Uservice user_service 在蛇形命名法中,各个单词之间通过下划线“\_”连接,比如`should_get_200_status_code_when_request_is_valid`、`CLIENT_CONNECT_SERVER_FAILURE`。 -蛇形命名法的优势是命名所需要的单词比较多的时候,比如我把上面的命名通过小驼峰命名法给大家看一下:“shouldGet200StatusCodoWhenRequestIsValid”。**感觉如何? 相比于使用蛇形命名法(snake_case)来说是不是不那么易读?\*\*** +蛇形命名法的优势是命名所需要的单词比较多的时候,比如我把上面的命名通过小驼峰命名法给大家看一下:“shouldGet200StatusCodoWhenRequestIsValid”。 + +感觉如何? 相比于使用蛇形命名法(snake_case)来说是不是不那么易读? 正例: @@ -103,29 +135,29 @@ void shouldGet200StatusCodoWhenRequestIsValid() { ### 串式命名法(kebab-case) -在串式命名法中,各个单词之间通过下划线“-”连接,比如`dubbo-registry`。 +在串式命名法中,各个单词之间通过连接符“-”连接,比如`dubbo-registry`。 建议项目文件夹名称使用串式命名法(kebab-case),比如 dubbo 项目的各个模块的命名是下面这样的。 -![](./pictures/dubbo-naming.png) +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/dubbo-naming.png) ## 常见命名规范 ### Java 语言基本命名规范 -**1.类名需要使用大驼峰命名法(UpperCamelCase)风格。方法名、参数名、成员变量、局部变量需要使用小驼峰命名法(lowerCamelCase)。** +**1、类名需要使用大驼峰命名法(UpperCamelCase)风格。方法名、参数名、成员变量、局部变量需要使用小驼峰命名法(lowerCamelCase)。** -**2.测试方法名、常量、枚举名称需要使用蛇形命名法(snake_case)**,比如`should_get_200_status_code_when_request_is_valid`、`CLIENT_CONNECT_SERVER_FAILURE`。并且,**测试方法名称要求全部小写,常量以及枚举名称需要全部大写。** +**2、测试方法名、常量、枚举名称需要使用蛇形命名法(snake_case)**,比如`should_get_200_status_code_when_request_is_valid`、`CLIENT_CONNECT_SERVER_FAILURE`。并且,**测试方法名称要求全部小写,常量以及枚举名称需要全部大写。** -**3.项目文件夹名称使用串式命名法(kebab-case),比如`dubbo-registry`。** +**3、项目文件夹名称使用串式命名法(kebab-case),比如`dubbo-registry`。** -**4.包名统一使用小写,尽量使用单个名词作为包名,各个单词通过 "." 分隔符连接,并且各个单词必须为单数。** +**4、包名统一使用小写,尽量使用单个名词作为包名,各个单词通过 "." 分隔符连接,并且各个单词必须为单数。** 正例: `org.apache.dubbo.common.threadlocal` 反例: ~~`org.apache_dubbo.Common.threadLocals`~~ -**5.抽象类命名使用 Abstract 开头**。 +**5、抽象类命名使用 Abstract 开头**。 ```java //为远程传输部分抽象出来的一个抽象类(出处:Dubbo源码) @@ -134,7 +166,7 @@ public abstract class AbstractClient extends AbstractEndpoint implements Client } ``` -**6.异常类命名使用 Exception 结尾。** +**6、异常类命名使用 Exception 结尾。** ```java //自定义的 NoSuchMethodException(出处:Dubbo源码) @@ -151,7 +183,7 @@ public class NoSuchMethodException extends RuntimeException { } ``` -**7.测试类命名以它要测试的类的名称开始,以 Test 结尾。** +**7、测试类命名以它要测试的类的名称开始,以 Test 结尾。** ```java //为 AnnotationUtils 类写的测试类(出处:Dubbo源码) @@ -166,19 +198,19 @@ POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架 ### 命名易读性规范 -**1.为了能让命名更加易懂和易读,尽量不要缩写/简写单词,除非这些单词已经被公认可以被这样缩写/简写。比如 `CustomThreadFactory` 不可以被写成 ~~`CustomTF` 。** +**1、为了能让命名更加易懂和易读,尽量不要缩写/简写单词,除非这些单词已经被公认可以被这样缩写/简写。比如 `CustomThreadFactory` 不可以被写成 ~~`CustomTF` 。** -**2.命名不像函数一样要尽量追求短,可读性强的名字优先于简短的名字,虽然可读性强的名字会比较长一点。** 这个对应我们上面说的第 1 点。 +**2、命名不像函数一样要尽量追求短,可读性强的名字优先于简短的名字,虽然可读性强的名字会比较长一点。** 这个对应我们上面说的第 1 点。 -**3.避免无意义的命名,你起的每一个名字都要能表明意思。** +**3、避免无意义的命名,你起的每一个名字都要能表明意思。** 正例:`UserService userService;` `int userCount`; 反例: ~~`UserService service`~~ ~~`int count`~~ -**4.避免命名过长(50 个字符以内最好),过长的命名难以阅读并且丑陋。** +**4、避免命名过长(50 个字符以内最好),过长的命名难以阅读并且丑陋。** -5.**不要使用拼音,更不要使用中文。** 注意:像 alibaba 、wuhan、taobao 这种国际通用名词可以当做英文来看待。 +**5、不要使用拼音,更不要使用中文。** 不过像 alibaba 、wuhan、taobao 这种国际通用名词可以当做英文来看待。 正例:discount @@ -186,7 +218,7 @@ POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架 ## Codelf:变量命名神器? -这是一个由国人开发的网站,网上有很多人称其为变量命名神器, Guide 在实际使用了几天之后感觉没那么好用。小伙伴们可以自行体验一下,然后再给出自己的判断。 +这是一个由国人开发的网站,网上有很多人称其为变量命名神器, 我在实际使用了几天之后感觉没那么好用。小伙伴们可以自行体验一下,然后再给出自己的判断。 Codelf 提供了在线网站版本,网址:[https://unbug.github.io/codelf/](https://unbug.github.io/codelf/),具体使用情况如下: @@ -198,17 +230,22 @@ Codelf 提供了在线网站版本,网址:[https://unbug.github.io/codelf/]( ![](pictures/vscode-codelf.png) -## 总结 - -Guide 制作了一个涵盖上面所有重要内容的思维导图,便于小伙伴们日后查阅。 - -![](pictures/naming-mindmap.png) - -## 其他推荐阅读 +## 相关阅读推荐 1. 《阿里巴巴 Java 开发手册》 2. 《Clean Code》 3. Google Java 代码指南:https://google.github.io/styleguide/javaguide.html#s5.1-identifier-name 4. 告别编码5分钟,命名2小时!史上最全的Java命名规范参考:https://www.cnblogs.com/liqiangchn/p/12000361.html +## 总结 + +作为一个合格的程序员,小伙伴们应该都知道代码表义的重要性。想要写出高质量代码,好的命名就是第一步! + +好的命名对于其他人(包括你自己)理解你的代码有着很大的帮助!你的代码越容易被理解,可维护性就越强,侧面也就说明你的代码设计的也就越好! + +在日常编码过程中,我们需要谨记常见命名规范比如类名需要使用大驼峰命名法、不要使用拼音,更不要使用中文......。 + +另外,国人开发的一个叫做 Codelf 的网站被很多人称为“变量命名神器”,当你为命名而头疼的时候,你可以去参考一下上面提供的一些命名示例。 + +最后,祝愿大家都不用再为命名而困扰!