Skip to content

Commit 8d77986

Browse files
committed
[docs update]java集合部分内容完善
1 parent f28c32f commit 8d77986

15 files changed

+122
-100
lines changed
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<mxfile host="Electron" modified="2022-08-11T10:29:38.995Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="EaH9fKq6YaRBX68HPPyn" version="13.4.5" type="device"><diagram id="mPrK13Eo7WhzfbSKrwnZ" name="Page-1">7Vtbd6M2EP41PGYPiIvh0XbsbNpkkx6nl32UQVw2MnKFHNv99RUgbED4kqxt3JSThzAjIcF8M6NvJKPow9nqjsJ5+Eg8hBWgeitFv1UA0Bxd5/9SzTrXOE4vVwQ08kSnrWIS/YOEUhXaReShpNKREYJZNK8qXRLHyGUVHaSULKvdfIKrs85hgCTFxIVY1v4ZeSzMtTbobfVfURSExcya5eQtM1h0Fm+ShNAjy5JKHyn6kBLC8qvZaohwarzCLvl94x2tmwejKGbH3ODfjqI7FuNZ3NcfXp71iT7q3xji2di6eGHk8fcXIqEsJAGJIR5ttQNKFrGH0lFVLm37PBAy50qNK38gxtYCTLhghKtCNsOilT8wXf+V3v/FLMTvYrhMuF1VpLWQfBIzMahmcXlj0bQxeUXMDcUMsm2EuRKyoC7aY5DCxyANENvTD+T9UmuVJhCWv0NkhviT8w4UYciit6o3QeGUwaafuLVPKVyXOsxJFLOkNPJzquAdRHzplnAuEV163Qdq/e293flF/gCFVHqTrSpzq3e4mNmGi9Vd5aMu16aL6f9JF7O0FnxMvPQbxAthhpeQ53445d5U976tb6UwLsOIockcZogt+Rq234/8COMhwYRmY+keRLbvpq7BKHlFpRbLtdHU/5gHvSHK0Gov5gU4atXYwBDycrtWacUCFJbWqQLUJjcpQfV+JAC4hnA/R9iCY1cG9dRx+3OA6K0s8auIldItl76XWrbJNhUuubwfDaJ2XSBK+W20ctGcRSQ+b34zke0ZTfnNBlPd+iBKx+c3UFtLrLbTm/VZs9t5yMZBdmCqNaTyyBR31cA6AVHQ5UDiRSI9axD5tovcRpIwtU3D/CDNPD6ITPPaoqgnofCpATCcHU5fAmDD3MoAAPNcCDgSAk8L9uQ/ohmh6+aQ4C/LqmavmjMmMarZXqggjoKYiy43IeL6QWq6yIW4LxpmkedlqbIJ52r6PEEufEfkaGYVOL0hcpwm4M6FW5E+S8D9EVG2gPgRumEUow67XdhZZtvYyVXqhEH39Ym/1BiTZQfdDuishnx5YegMCbr7pw6vHXj1tNbxMg8zDBR7/fSAYAuAB5NwQzlKAKb6Z8g4KHGmAaq+gbU4FACnMfpB/l0yqdlg0UJ3Ypreq9P0vJ6QaPrBgaw6pzwz39dsyRH6SZLGFYm7+N2Vb1unOUYrhwbXfC5VmPrwzpV5ZCa50M6VTFiHIXJfOU5Avcwmlu/7oLn886ypZZ49vG60ev3XsD5uDrwvUoAb3bnvjg3fw/FlXVd8yUXF77F78QhD1o4I6zlT9ewbLJpxbQEGjiCgnyrHAbXGIRo2GZv3uIyzYWBdGoOWowBcHwTyTi+vm3fmpP8h9+YVSq1Cctom30CumYYYJsk3wsapoTr89uHXa714AvLm/uS3hw61vaj12kZNlwulcYRRF3THwGc3HWleFj75ZFlCqttxLM1Q2yi8savkxa4ffR674ygNdOlfGMhHBX0asZCPG7ldFO+p2tpnPrpctWXMZwgT1kG3B7r2SY8uF3v3GKMA4peQIuhNGGSow7CMoXl1FEgmrvfcOqvspymD1FJJh+AeBDdrZnvnNqBjQT/DgoBhfDkRD2oY6nxM6GX44n4N1F9NL/wG2MQfsTVt+ChDGZkKp2f9oTIylMFQcXqZZqwM+MU40/R/gW/wLv3oTwEWTiN6Siv+Y/29SL+Yy0C/STLU+7yDZs1XGcBFO78Ksv+jnjK4VRwtvegPFMfZTPWDT5V9X/jFvdZ8guEU4QF0X4NMX5+8viHJ5dK24yD7y8bkq1+aNBtCohIsOzJQQ8js/sl27bDLAA1JqdcQQtr7kxIXt99P5m67/QpVH/0L</diagram></mxfile>
1+
<mxfile host="Electron" modified="2022-08-11T12:50:32.532Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="EW2-PGcUJKY6Z6AQ4-jf" version="13.4.5" type="device"><diagram id="mPrK13Eo7WhzfbSKrwnZ" name="Page-1">7Vtde6o4EP41XPY8Er4v1WpPd9vT7mP341xGCMJpJG6IVffXb4CgQPCjPSJul6cXZSYhgXlnJu8komjD+fqOwkXwSDyEFdDz1op2qwCgOprG/yWaTaZxHCtTzGjoiU47xST8BwllT2iXoYfiUkdGCGbhoqx0SRQhl5V0kFKyKnfzCS7PuoAzJCkmLsSy9s/QY0GmtYG1039F4SzIZ1ZNJ2uZw7yzeJM4gB5ZFVTaSNGGlBCWXc3XQ4QT4+V2ye4b72ndPhhFETvlBv92FN6xCM+jvvbw8qxNtFH/RhfPxjb5CyOPv78QCWUBmZEI4tFOO6BkGXkoGbXHpV2fB0IWXKly5Q/E2EaACZeMcFXA5li08gemm7+S+78YufhdDJcKt+uStBGSTyImBlVNLm8tmjTGr4i5gZhBto0wV0yW1EUHDJL7GKQzxA70A1m/xFqFCYTl7xCZI/7kvANFGLLwrexNUDjlbNtP3NqnFG4KHRYkjFhcGPk5UfAOIr40UziXiC6t6gOV/vbB7vwie4BcKrzJTpW61TtczGjDxaqu8lGXa9PFtP+ki5lqCz4mXvoN4qUww0vAcz+ccm+qet/OtxIYV0HI0GQBU8RWfA077Ed+iPGQYELTsTQPItt3E9dglLyiQovp2mjqf8yD3hBlaH0Q8xycXtnYQBfyardWqfkCFBTWqRzUOjcpQPV+JAC4hnBvImzBqStD79xx+3OAaK0s8euQFdItl74XWnbJNhEuubyfDKJ6XSBK+W20dtGChSRqNr8ZyPb0uvxmg6lmfhCl0/MbqKwlZtvpzfys2a0ZsnGUHRi9ClJZZIq7KmCdgShociDxIpE2GkS+7SK3liRMbUM3PkgzTw8iw7i2KLIkFD41ALqzx+kLAGyZWxEAYDSFgCMh8LRkT/4jmhO6qQ8J/rKsbPayOSMSoYrthQricBZx0eUmRFw/SEwXuhD3RcM89Lw0VdbhXE6fZ8iF74gc1SgDp9VEjlMHXFO45emzANwfIWVLiB+hG4QR6rDbh51ptI2dXKVOGHRfn/hLjTFZddDtgc6syZcXhk6XoLt/6vDag5elto6XcZxhoMjrJwcEOwA8GAdbylEAMNE/Q8ZBiVIN6GlbWPNDAXAeox/l3wWTGjUWzXVnpulWlaZn9YRE048OZFY5ZcN8X7UlR+jHcRJXJOrid1++bZ3m6K0cGlzzuVRu6uM7V8aJmeRCO1cyYR0GyH3lOIHeZTaxfN8H9eWfZ05No/HwulGr9V/N+rg98L5IAa535757NnyPx5d5XfElFxW/R+7FIwyZeyLMcqa9xjdYVP3aAgycQEA/VY4DvQqHqNlkrN/j0hvDwLw0Bi1HAbg+COSdXl43781J/0PuzSuUSoXktE2+gVwzDTGM42+EjRNDdfgdws9qvXgC8ub+5LeHDrWDqFlto6bJhdI4xKgLulPgs+uONC8Ln3yyLCHV7TgWZqhsFN7YZfJiV48+T91xlAa69C8M5KOCPg1ZwMcN3S6KD1Rt7TMfTa7aUuYzhDHroDsAXfukR5OLvXuM0Qzil4Ai6E0YZKjDsIihcXUUSCau99w66/SnKYPEUnGH4AEEt2tme+c2oGNBP8OCgK5/ORMPqhmqOSb0Mnxxv856vxpe8A2wiT9iG1rzUYYyMhROz/pDZaQrg6HiWKlmrAz4xTjV9H+Bb/Au+ehPASZOInpKS/5j/r1MvphLQb+JU9T7vINqLtYpwHk7v5ql/0eWMrhVHDW56A8Ux9lO9YNPlX5f+MW91nyC4RThAXRfZ6m+Onl1Q5LLhW3HQfqXjslXvyRp1oREKVjOkJRA5bBLBzVJyaoJIfX9SYmLu+8nM7fdfYWqjf4F</diagram></mxfile>

docs/java/basis/java-basic-questions-03.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ head:
1616

1717
**Java 异常类层次结构图概览**
1818

19-
![Java 异常类层次结构图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/basis/types-of-exceptions-in-java.jpg)
19+
![Java 异常类层次结构图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/basis/types-of-exceptions-in-java.png)
2020

2121
### Exception 和 Error 有什么区别?
2222

docs/java/collection/concurrent-hash-map-source-code.md

+34-36
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@ tag:
1414

1515
### 1. 存储结构
1616

17-
> 下图存在两个笔误 : Segmeng -> Segment ; HashEntity -> HashEntry
17+
![Java 7 ConcurrentHashMap 存储结构](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/collection/java7_concurrenthashmap.png)
1818

19-
![Java 7 ConcurrentHashMap 存储结构](./images/java7_concurrenthashmap.png)
20-
21-
Java 7 中 `ConcurrentHashMap` 的存储结构如上图,`ConcurrnetHashMap` 由很多个 `Segment` 组合,而每一个 `Segment` 是一个类似于 HashMap 的结构,所以每一个 `HashMap` 的内部可以进行扩容。但是 `Segment` 的个数一旦**初始化就不能改变**,默认 `Segment` 的个数是 16 个,你也可以认为 `ConcurrentHashMap` 默认支持最多 16 个线程并发。
19+
Java 7 中 `ConcurrentHashMap` 的存储结构如上图,`ConcurrnetHashMap` 由很多个 `Segment` 组合,而每一个 `Segment` 是一个类似于 `HashMap` 的结构,所以每一个 `HashMap` 的内部可以进行扩容。但是 `Segment` 的个数一旦**初始化就不能改变**,默认 `Segment` 的个数是 16 个,你也可以认为 `ConcurrentHashMap` 默认支持最多 16 个线程并发。
2220

2321
### 2. 初始化
2422

25-
通过 ConcurrentHashMap 的无参构造探寻 ConcurrentHashMap 的初始化流程。
23+
通过 `ConcurrentHashMap` 的无参构造探寻 `ConcurrentHashMap` 的初始化流程。
2624

2725
```java
2826
/**
@@ -101,11 +99,11 @@ public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLe
10199
总结一下在 Java 7 中 ConcurrnetHashMap 的初始化逻辑。
102100

103101
1. 必要参数校验。
104-
2. 校验并发级别 concurrencyLevel 大小,如果大于最大值,重置为最大值。无参构造**默认值是 16.**
105-
3. 寻找并发级别 concurrencyLevel 之上最近的 **2 的幂次方**值,作为初始化容量大小,**默认是 16**
106-
4. 记录 segmentShift 偏移量,这个值为【容量 = 2 的N次方】中的 N,在后面 Put 时计算位置时会用到。**默认是 32 - sshift = 28**.
107-
5. 记录 segmentMask,默认是 ssize - 1 = 16 -1 = 15.
108-
6. **初始化 segments[0]****默认大小为 2****负载因子 0.75****扩容阀值是 2*0.75=1.5**,插入第二个值时才会进行扩容。
102+
2. 校验并发级别 `concurrencyLevel` 大小,如果大于最大值,重置为最大值。无参构造**默认值是 16.**
103+
3. 寻找并发级别 `concurrencyLevel` 之上最近的 **2 的幂次方**值,作为初始化容量大小,**默认是 16**
104+
4. 记录 `segmentShift` 偏移量,这个值为【容量 = 2 的N次方】中的 N,在后面 Put 时计算位置时会用到。**默认是 32 - sshift = 28**.
105+
5. 记录 `segmentMask`,默认是 ssize - 1 = 16 -1 = 15.
106+
6. **初始化 `segments[0]`****默认大小为 2****负载因子 0.75****扩容阀值是 2*0.75=1.5**,插入第二个值时才会进行扩容。
109107

110108
### 3. put
111109

@@ -179,23 +177,23 @@ private Segment<K,V> ensureSegment(int k) {
179177
}
180178
```
181179

182-
上面的源码分析了 ConcurrentHashMap 在 put 一个数据时的处理流程,下面梳理下具体流程。
180+
上面的源码分析了 `ConcurrentHashMap` 在 put 一个数据时的处理流程,下面梳理下具体流程。
183181

184-
1. 计算要 put 的 key 的位置,获取指定位置的 Segment。
182+
1. 计算要 put 的 key 的位置,获取指定位置的 `Segment`
185183

186-
2. 如果指定位置的 Segment 为空,则初始化这个 Segment.
184+
2. 如果指定位置的 `Segment` 为空,则初始化这个 `Segment`.
187185

188186
**初始化 Segment 流程:**
189187

190-
1. 检查计算得到的位置的 Segment 是否为null.
191-
2. 为 null 继续初始化,使用 Segment[0] 的容量和负载因子创建一个 HashEntry 数组。
192-
3. 再次检查计算得到的指定位置的 Segment 是否为null.
193-
4. 使用创建的 HashEntry 数组初始化这个 Segment.
194-
5. 自旋判断计算得到的指定位置的 Segment 是否为null,使用 CAS 在这个位置赋值为 Segment.
188+
1. 检查计算得到的位置的 `Segment` 是否为null.
189+
2. 为 null 继续初始化,使用 `Segment[0]` 的容量和负载因子创建一个 `HashEntry` 数组。
190+
3. 再次检查计算得到的指定位置的 `Segment` 是否为null.
191+
4. 使用创建的 `HashEntry` 数组初始化这个 Segment.
192+
5. 自旋判断计算得到的指定位置的 `Segment` 是否为null,使用 CAS 在这个位置赋值为 `Segment`.
195193

196-
3. Segment.put 插入 key,value 值。
194+
3. `Segment.put` 插入 key,value 值。
197195

198-
上面探究了获取 Segment 段和初始化 Segment 段的操作。最后一行的 Segment 的 put 方法还没有查看,继续分析。
196+
上面探究了获取 `Segment` 段和初始化 `Segment` 段的操作。最后一行的 `Segment` 的 put 方法还没有查看,继续分析。
199197

200198
```java
201199
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
@@ -249,29 +247,29 @@ final V put(K key, int hash, V value, boolean onlyIfAbsent) {
249247
}
250248
```
251249

252-
由于 Segment 继承了 ReentrantLock,所以 Segment 内部可以很方便的获取锁,put 流程就用到了这个功能。
250+
由于 `Segment` 继承了 `ReentrantLock`,所以 `Segment` 内部可以很方便的获取锁,put 流程就用到了这个功能。
253251

254-
1. tryLock() 获取锁,获取不到使用 **`scanAndLockForPut`** 方法继续获取。
252+
1. `tryLock()` 获取锁,获取不到使用 **`scanAndLockForPut`** 方法继续获取。
255253

256-
2. 计算 put 的数据要放入的 index 位置,然后获取这个位置上的 HashEntry 。
254+
2. 计算 put 的数据要放入的 index 位置,然后获取这个位置上的 `HashEntry`
257255

258-
3. 遍历 put 新元素,为什么要遍历?因为这里获取的 HashEntry 可能是一个空元素,也可能是链表已存在,所以要区别对待。
256+
3. 遍历 put 新元素,为什么要遍历?因为这里获取的 `HashEntry` 可能是一个空元素,也可能是链表已存在,所以要区别对待。
259257

260-
如果这个位置上的 **HashEntry 不存在**
258+
如果这个位置上的 **`HashEntry` 不存在**
261259

262260
1. 如果当前容量大于扩容阀值,小于最大容量,**进行扩容**
263261
2. 直接头插法插入。
264262

265-
如果这个位置上的 **HashEntry 存在**
263+
如果这个位置上的 **`HashEntry` 存在**
266264

267-
1. 判断链表当前元素 Key 和 hash 值是否和要 put 的 key 和 hash 值一致。一致则替换值
265+
1. 判断链表当前元素 key 和 hash 值是否和要 put 的 key 和 hash 值一致。一致则替换值
268266
2. 不一致,获取链表下一个节点,直到发现相同进行值替换,或者链表表里完毕没有相同的。
269267
1. 如果当前容量大于扩容阀值,小于最大容量,**进行扩容**
270268
2. 直接链表头插法插入。
271269

272270
4. 如果要插入的位置之前已经存在,替换后返回旧值,否则返回 null.
273271

274-
这里面的第一步中的 scanAndLockForPut 操作这里没有介绍,这个方法做的操作就是不断的自旋 `tryLock()` 获取锁。当自旋次数大于指定次数时,使用 `lock()` 阻塞获取锁。在自旋时顺表获取下 hash 位置的 HashEntry。
272+
这里面的第一步中的 `scanAndLockForPut` 操作这里没有介绍,这个方法做的操作就是不断的自旋 `tryLock()` 获取锁。当自旋次数大于指定次数时,使用 `lock()` 阻塞获取锁。在自旋时顺表获取下 hash 位置的 `HashEntry`
275273

276274
```java
277275
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
@@ -311,7 +309,7 @@ private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
311309

312310
### 4. 扩容 rehash
313311

314-
ConcurrentHashMap 的扩容只会扩容到原来的两倍。老数组里的数据移动到新的数组时,位置要么不变,要么变为 index+ oldSize,参数里的 node 会在扩容之后使用链表**头插法**插入到指定位置。
312+
`ConcurrentHashMap` 的扩容只会扩容到原来的两倍。老数组里的数据移动到新的数组时,位置要么不变,要么变为 `index+ oldSize`,参数里的 node 会在扩容之后使用链表**头插法**插入到指定位置。
315313

316314
```java
317315
private void rehash(HashEntry<K,V> node) {
@@ -406,7 +404,7 @@ public V get(Object key) {
406404

407405
### 1. 存储结构
408406

409-
![Java8 ConcurrentHashMap 存储结构(图片来自 javadoop)](./images/java8_concurrenthashmap.png)
407+
![Java8 ConcurrentHashMap 存储结构(图片来自 javadoop)](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/collection/java8_concurrenthashmap.png)
410408

411409
可以发现 Java8 的 ConcurrentHashMap 相对于 Java7 来说变化比较大,不再是之前的 **Segment 数组 + HashEntry 数组 + 链表**,而是 **Node 数组 + 链表 / 红黑树**。当冲突链表达到一定长度时,链表会转换成红黑树。
412410

@@ -442,7 +440,7 @@ private final Node<K,V>[] initTable() {
442440
}
443441
```
444442

445-
从源码中可以发现 ConcurrentHashMap 的初始化是通过**自旋和 CAS** 操作完成的。里面需要注意的是变量 `sizeCtl` ,它的值决定着当前的初始化状态。
443+
从源码中可以发现 `ConcurrentHashMap` 的初始化是通过**自旋和 CAS** 操作完成的。里面需要注意的是变量 `sizeCtl` ,它的值决定着当前的初始化状态。
446444

447445
1. -1 说明正在初始化
448446
2. -N 说明有N-1个线程正在进行扩容
@@ -541,7 +539,7 @@ final V putVal(K key, V value, boolean onlyIfAbsent) {
541539

542540
5. 如果都不满足,则利用 synchronized 锁写入数据。
543541

544-
6. 如果数量大于 `TREEIFY_THRESHOLD` 则要执行树化方法,在treeifyBin中会首先判断当前数组长度≥64时才会将链表转换为红黑树。
542+
6. 如果数量大于 `TREEIFY_THRESHOLD` 则要执行树化方法,`treeifyBin` 中会首先判断当前数组长度≥64时才会将链表转换为红黑树。
545543

546544
### 4. get
547545

@@ -583,12 +581,12 @@ public V get(Object key) {
583581

584582
总结:
585583

586-
总的来说 ConcurrentHashMap 在 Java8 中相对于 Java7 来说变化还是挺大的,
584+
总的来说 `ConcurrentHashMap` 在 Java8 中相对于 Java7 来说变化还是挺大的,
587585

588586
## 3. 总结
589587

590-
Java7 中 ConcurrentHashMap 使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 Segment 都是一个类似 HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变。
588+
Java7 中 `ConcurrentHashMap` 使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 `Segment` 都是一个类似 `HashMap` 数组的结构,它可以扩容,它的冲突会转化为链表。但是 `Segment` 的个数一但初始化就不能改变。
591589

592-
Java8 中的 ConcurrentHashMap 使用的 Synchronized 锁加 CAS 的机制。结构也由 Java7 中的 **Segment 数组 + HashEntry 数组 + 链表** 进化成了 **Node 数组 + 链表 / 红黑树**,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
590+
Java8 中的 `ConcurrentHashMap` 使用的 `Synchronized` 锁加 CAS 的机制。结构也由 Java7 中的 **`Segment` 数组 + `HashEntry` 数组 + 链表** 进化成了 **Node 数组 + 链表 / 红黑树**,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
593591

594-
有些同学可能对 Synchronized 的性能存在疑问,其实 Synchronized 锁自从引入锁升级策略后,性能不再是问题,有兴趣的同学可以自己了解下 Synchronized 的**锁升级**
592+
有些同学可能对 `Synchronized` 的性能存在疑问,其实 `Synchronized` 锁自从引入锁升级策略后,性能不再是问题,有兴趣的同学可以自己了解下 `Synchronized`**锁升级**
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<mxfile host="Electron" modified="2022-08-11T23:13:39.447Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="1SSSpNWCcrVkbHT7kLUn" version="13.4.5" type="device"><diagram id="oZhQm8CpKlBOiSaDXld-" name="Page-1">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</diagram></mxfile>
Binary file not shown.

0 commit comments

Comments
 (0)