1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

Merge branch 'Snailclimb:master' into master

This commit is contained in:
zhaoweiwei 2021-05-31 16:27:38 +08:00
commit 44ee9c052b
29 changed files with 243 additions and 14 deletions

View File

@ -1,10 +1,8 @@
如果你不知道该学习什么的话,请看 [Java 学习线路图是怎样的?]( https://www.zhihu.com/question/56110328/answer/869069586) (原创不易,欢迎点赞),这是 2021 最新最完善的 Java 学习路线!
👉 如果你不知道该学习什么的话,请看 [Java 学习线路图是怎样的?]( https://www.zhihu.com/question/56110328/answer/869069586) (原创不易,欢迎点赞),这是 2021 最新最完善的 Java 学习路线!
👍推荐 [在线阅读](https://snailclimb.gitee.io/javaguide) (Github 访问速度比较慢可能会导致部分图片无法刷新出来)
👉 推荐 [在线阅读](https://snailclimb.gitee.io/javaguide) (Github 访问速度比较慢可能会导致部分图片无法刷新出来)
👍推荐[2021最新实战项目源码下载](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100018862&idx=1&sn=858e00b60c6097e3ba061e79be472280&chksm=4ea1856579d60c73224e4d852af6b0188c3ab905069fc28f4b293963fd1ee55d2069fb229848#rd)
书单已经被移动到[awesome-cs](https://github.com/CodingDocs/awesome-cs) 这个仓库。
👉 书单已经被移动到[awesome-cs](https://github.com/CodingDocs/awesome-cs) 这个仓库。
> 1. **介绍**:关于 JavaGuide 的相关介绍请看:[关于 JavaGuide 的一些说明](https://www.yuque.com/snailclimb/dr6cvl/mr44yt) 。
> 2. **PDF版本** [《JavaGuide 面试突击版》PDF 版本](#公众号) 。[图解计算机基础 PDF 版](#优质原创PDF资源)。
@ -111,6 +109,7 @@
- **图解数据结构:**
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)
### 算法

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,189 @@
## 什么是堆
堆是一种满足以下条件的树:
堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。或者说,任意一个节点的值都大于等于(或小于等于)所有子节点的值。
> 大家可以把堆(最大堆)理解为一个公司,这个公司很公平,谁能力强谁就当老大,不存在弱的人当老大,老大手底下的人一定不会比他强。这样有助于理解后续堆的操作。
**!!!特别提示:**
- 很多博客说堆是完全二叉树,其实并非如此,**堆不一定是完全二叉树**,只是为了方便存储和索引,我们通常用完全二叉树的形式来表示堆,事实上,广为人知的斐波那契堆和二项堆就不是完全二叉树,它们甚至都不是二叉树。
- **二叉**)堆是一个数组,它可以被看成是一个 **近似的完全二叉树**。——《算法导论》第三版
大家可以尝试判断下面给出的图是否是二叉树?
![](pictures/堆/堆1.png)
第1个和第2个是堆。第1个是最大堆每个节点都比子树中所有节点大。第2个是最小堆每个节点都比子树中所有节点小。
第3个不是第三个中根结点1比2和15小而15却比3大19比5大不满足堆的性质。
## 堆的用途
当我们只关心所有数据中的最大值或者最小值,存在多次获取最大值或者最小值,多次插入或删除数据时,就可以使用堆。
有小伙伴可能会想到用有序数组,初始化一个有序数组时间复杂度是 `O(nlog(n))`,查找最大值或者最小值时间复杂度都是 `O(1)`,但是,涉及到更新(插入或删除)数据时,时间复杂度为 `O(n)`,即使是使用复杂度为 `O(log(n))` 的二分法找到要插入或者删除的数据,在移动数据时也需要 `O(n)` 的时间复杂度。
**相对于有序数组而言,堆的主要优势在于更新数据效率较高。** 堆的初始化时间复杂度为 `O(nlog(n))`,堆可以做到`O(1)`时间复杂度取出最大值或者最小值,`O(log(n))`时间复杂度插入或者删除数据,具体操作在后续章节详细介绍。
## 堆的分类
堆分为 **最大堆****最小堆**。二者的区别在于节点的排序方式。
- **最大堆** :堆中的每一个节点的值都大于等于子树中所有节点的值
- **最小堆** :堆中的每一个节点的值都小于等于子树中所有节点的值
如下图所示图1是最大堆图2是最小堆
![](pictures/堆/堆2.png)
## 堆的存储
之前介绍树的时候说过由于完全二叉树的优秀性质利用数组存储二叉树即节省空间又方便索引若根结点的序号为1那么对于树中任意节点i其左子节点序号为 `2\*i`,右子节点序号为 `2\*i+1`)。
为了方便存储和索引,(二叉)堆可以用完全二叉树的形式进行存储。存储的方式如下图所示:
![堆的存储](pictures/堆/堆的存储.png)
## 堆的操作
堆的更新操作主要包括两种 : **插入元素****删除堆顶元素**。操作过程需要着重掌握和理解。
> 在进入正题之前,再重申一遍,堆是一个公平的公司,有能力的人自然会走到与他能力所匹配的位置
### 插入元素
> 插入元素,作为一个新入职的员工,初来乍到,这个员工需要从基层做起
**1.将要插入的元素放到最后**
![堆-插入元素-1](pictures/堆/堆-插入元素1.png)
> 有能力的人会逐渐升职加薪,是金子总会发光的!!!
**2.从底向上,如果父结点比该元素大,则该节点和父结点交换,直到无法交换**
![堆-插入元素2](pictures/堆/堆-插入元素2.png)
![堆-插入元素3](pictures/堆/堆-插入元素3.png)
### 删除堆顶元素
根据堆的性质可知,最大堆的堆顶元素为所有元素中最大的,最小堆的堆顶元素是所有元素中最小的。当我们需要多次查找最大元素或者最小元素的时候,可以利用堆来实现。
删除堆顶元素后,为了保持堆的性质,需要对堆的结构进行调整,我们将这个过程称之为"**堆化**",堆化的方法分为两种:
- 一种是自底向上的堆化,上述的插入元素所使用的就是自底向上的堆化,元素从最底部向上移动。
- 另一种是自顶向下堆化,元素由最顶部向下移动。在讲解删除堆顶元素的方法时,我将阐述这两种操作的过程,大家可以体会一下二者的不同。
#### 自底向上堆化
> 在堆这个公司中,会出现老大离职的现象,老大离职之后,他的位置就空出来了
首先删除堆顶元素使得数组中下标为1的位置空出。
![删除堆顶元素1](pictures/堆/删除堆顶元素1.png)
> 那么他的位置由谁来接替呢,当然是他的直接下属了,谁能力强就让谁上呗
比较根结点的左子节点和右子节点也就是下标为2,3的数组元素将较大的元素填充到根结点(下标为1)的位置。
![删除堆顶元素2](pictures/堆/删除堆顶元素2.png)
> 这个时候又空出一个位置了,老规矩,谁有能力谁上
一直循环比较空出位置的左右子节点,并将较大者移至空位,直到堆的最底部
![删除堆顶元素3](pictures/堆/删除堆顶元素3.png)
这个时候已经完成了自底向上的堆化,没有元素可以填补空缺了,但是,我们可以看到数组中出现了“气泡”,这会导致存储空间的浪费。接下来我们试试自顶向下堆化。
#### 自顶向下堆化
自顶向下的堆化用一个词形容就是“石沉大海”,那么第一件事情,就是把石头抬起来,从海面扔下去。这个石头就是堆的最后一个元素,我们将最后一个元素移动到堆顶。
![删除堆顶元素4](pictures/堆/删除堆顶元素4.png)
然后开始将这个石头沉入海底,不停与左右子节点的值进行比较,和较大的子节点交换位置,直到无法交换位置。
![删除堆顶元素5](pictures/堆/删除堆顶元素5.png)
![删除堆顶元素6](pictures/堆/删除堆顶元素6.png)
### 堆的操作总结
- **插入元素** :先将元素放至数组末尾,再自底向上堆化,将末尾元素上浮
- **删除堆顶元素** :删除堆顶元素,将末尾元素放至堆顶,再自顶向下堆化,将堆顶元素下沉。也可以自底向上堆化,只是会产生“气泡”,浪费存储空间。最好采用自顶向下堆化的方式。
## 堆排序
堆排序的过程分为两步:
- 第一步是建堆,将一个无序的数组建立为一个堆
- 第二步是排序,将堆顶元素取出,然后对剩下的元素进行堆化,反复迭代,直到所有元素被取出为止。
### 建堆
如果你已经足够了解堆化的过程,那么建堆的过程掌握起来就比较容易了。建堆的过程就是一个对所有非叶节点的自顶向下堆化过程。
首先要了解哪些是非叶节点最后一个节点的父结点及它之前的元素都是非叶节点。也就是说如果节点个数为n那么我们需要对n/2到1的节点进行自顶向下沉底堆化。
具体过程如下图:
![建堆1](pictures/堆/建堆1.png)
将初始的无序数组抽象为一棵树图中的节点个数为6所以4,5,6节点为叶节点1,2,3节点为非叶节点所以要对1-3号节点进行自顶向下沉底堆化注意顺序是从后往前堆化从3号节点开始一直到1号节点。
3号节点堆化结果
![建堆1](pictures/堆/建堆2.png)
2号节点堆化结果
![建堆1](pictures/堆/建堆3.png)
1号节点堆化结果
![建堆1](pictures/堆/建堆4.png)
至此,数组所对应的树已经成为了一个最大堆,建堆完成!
### 排序
由于堆顶元素是所有元素中最大的,所以我们重复取出堆顶元素,将这个最大的堆顶元素放至数组末尾,并对剩下的元素进行堆化即可。
现在思考两个问题:
- 删除堆顶元素后需要执行自顶向下(沉底)堆化还是自底向上(上浮)堆化?
- 取出的堆顶元素存在哪,新建一个数组存?
先回答第一个问题,我们需要执行自顶向下(沉底)堆化,这个堆化一开始要将末尾元素移动至堆顶,这个时候末尾的位置就空出来了,由于堆中元素已经减小,这个位置不会再被使用,所以我们可以将取出的元素放在末尾。
机智的小伙伴已经发现了,这其实是做了一次交换操作,将堆顶和末尾元素调换位置,从而将取出堆顶元素和堆化的第一步(将末尾元素放至根结点位置)进行合并。
详细过程如下图所示:
取出第一个元素并堆化:
![堆排序1](pictures/堆/堆排序1.png)
取出第二个元素并堆化:
![堆排序2](pictures/堆/堆排序2.png)
取出第三个元素并堆化:
![堆排序3](pictures/堆/堆排序3.png)
取出第四个元素并堆化:
![堆排序4](pictures/堆/堆排序4.png)
取出第五个元素并堆化:
![堆排序5](pictures/堆/堆排序5.png)
取出第六个元素并堆化:
![堆排序6](pictures/堆/堆排序6.png)
堆排序完成!

View File

@ -733,6 +733,24 @@ public class ArrayList<E> extends AbstractList<E>
#### 3.3.1. `System.arraycopy()` 方法
源码:
```java
// 我们发现 arraycopy 是一个 native 方法,接下来我们解释一下各个参数的具体意义
/**
* 复制数组
* @param src 源数组
* @param srcPos 源数组中的起始位置
* @param dest 目标数组
* @param destPos 目标数组中的起始位置
* @param length 要复制的数组元素的数量
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
```
场景:
```java
/**
* 在此列表中的指定位置插入指定的元素。
@ -781,6 +799,21 @@ public class ArraycopyTest {
#### 3.3.2. `Arrays.copyOf()`方法
源码:
```java
public static int[] copyOf(int[] original, int newLength) {
// 申请一个新的数组
int[] copy = new int[newLength];
// 调用System.arraycopy,将源数组中的数据进行拷贝,并返回新的数组
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
```
场景:
```java
/**
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。

View File

@ -594,10 +594,10 @@ void rotate(List list, int distance)//旋转。当distance为正数时将list
int binarySearch(List list, Object key)//对List进行二分查找返回索引注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序返回最大元素排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引找不到则返回-1类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引找不到则返回-1类比int lastIndexOfSubList(List source, list target)
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素
```
### 1.5.3. 同步控制
@ -619,4 +619,4 @@ synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的
**《Java 面试突击》:** Java 程序员面试必备的《Java 面试突击》V3.0 PDF 版本扫码关注下面的公众号,在后台回复 **"面试突击"** 即可免费领取!
![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/format,png.jpeg)
![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/format,png.jpeg)

View File

@ -907,7 +907,7 @@ AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方
CAS 的原理是拿期望的值和原本的一个值作比较如果相同则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是 valueOffset。另外 value 是一个 volatile 变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。
关于 Atomic 原子类这部分更多内容可以查看我的这篇文章:并发编程面试必备:[JUC 中的 Atomic 原子类总结](https://mp.weixin.qq.com/s/joa-yOiTrYF67bElj8xqvg)
关于 Atomic 原子类这部分更多内容可以查看我的这篇文章:并发编程面试必备:[JUC 中的 Atomic 原子类总结](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484834&idx=1&sn=7d3835091af8125c13fc6db765f4c5bd&source=41#wechat_redirect)
## 6. AQS

View File

@ -53,6 +53,14 @@ CAP 理论这节我们也说过了:
>
> **业界比较推崇是最终一致性级别,但是某些对数据一致要求十分严格的场景比如银行转账还是要保证强一致性。**
那实现最终一致性的具体方式是什么呢? [《分布式协议与算法实战》](http://gk.link/a/10rZM) 中是这样介绍:
> - **读时修复** : 在读取数据时,检测数据的不一致,进行修复。比如 Cassandra 的 Read Repair 实现,具体来说,在向 Cassandra 系统查询数据的时候,如果检测到不同节点 的副本数据不一致,系统就自动修复数据。
> - **写时修复** : 在写入数据,检测数据的不一致时,进行修复。比如 Cassandra 的 Hinted Handoff 实现。具体来说Cassandra 集群的节点之间远程写数据的时候,如果写失败 就将数据缓存下来,然后定时重传,修复数据的不一致性。
> - **异步修复** : 这个是最常用的方式,通过定时对账检测副本数据的一致性,并修复。
比较推荐 **写时修复**,这种方式对性能消耗比较低。
### 总结
**ACID 是数据库事务完整性的理论CAP 是分布式系统设计理论BASE 是 CAP 理论中 AP 方案的延伸。**

View File

@ -46,13 +46,13 @@ CAP 理论的提出者布鲁尔在提出 CAP 猜想的时候,并没有详细
>
> 简而言之就是CAP 理论中分区容错性 P 是一定要满足的,在此基础上,只能满足可用性 A 或者一致性 C。
因此,**分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。**
因此,**分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。** 比如 ZooKeeper、HBase 就是 CP 架构Cassandra、Eureka 就是 AP 架构Nacos 不仅支持 CP 架构也支持 AP 架构。
**为啥无同时保证 CA 呢?**
**为啥不可能选择 CA 架构呢?** 举个例子:若系统出现“分区”,系统中的某个节点在进行写操作。为了保证 C 必须要禁止其他节点的读写操作,这就和 A 发生冲突了。如果为了保证 A其他节点的读写操作正常的话那就和 C 发生冲突了。
举个例子:若系统出现“分区”,系统中的某个节点在进行写操作。为了保证 C 必须要禁止其他节点的读写操作,这就和 A 发生冲突了。如果为了保证 A其他节点的读写操作正常的话那就和 C 发生冲突了。
**选择 CP 还是 AP 的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP 。**
**选择的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP 。**
另外,需要补充说明的一点是: **如果网络分区正常的话(系统在绝大部分时候所处的状态),也就说不需要保证 P 的时候C 和 A 能够同时保证。**
### CAP 实际应用案例