auto commit

This commit is contained in:
CyC2018 2018-08-13 20:44:07 +08:00
parent 7746f5d72f
commit ddb9e86224

View File

@ -21,7 +21,7 @@
# 一、概览 # 一、概览
容器主要包括 Collection 和 Map 两种Collection 又包含了 List、Set 以及 Queue 容器主要包括 Collection 和 Map 两种Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表
## Collection ## Collection
@ -29,9 +29,9 @@
### 1. Set ### 1. Set
- HashSet基于哈希表实现支持快速查找。但不支持有序性操作例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的 - TreeSet基于红黑树实现支持有序性操作例如根据一个范围查找元素的操作。但是查找效率不如 HashSetHashSet 查找的时间复杂度为 O(1)TreeSet 则为 O(logN)
- TreeSet基于红黑树实现支持有序性操作但是查找效率不如 HashSetHashSet 查找时间复杂度为 O(1)TreeSet 则为 O(logN) - HashSet基于哈希表实现支持快速查找但不支持有序性操作。并且失去了元素的插入顺序信息也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的
- LinkedHashSet具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。 - LinkedHashSet具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
@ -53,13 +53,14 @@
<div align="center"> <img src="../pics//SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png"/> </div><br> <div align="center"> <img src="../pics//SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png"/> </div><br>
- HashMap基于哈希表实现 - TreeMap基于红黑树实现。
- HashMap基于哈希表实现。
- HashTable和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。 - HashTable和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
- LinkedHashMap使用双向链表来维护元素的顺序顺序为插入顺序或者最近最少使用LRU顺序。 - LinkedHashMap使用双向链表来维护元素的顺序顺序为插入顺序或者最近最少使用LRU顺序。
- TreeMap基于红黑树实现。
# 二、容器中的设计模式 # 二、容器中的设计模式
@ -296,14 +297,14 @@ public synchronized E get(int index) {
} }
``` ```
### 2. 与 ArrayList 的区别 ### 2. 与 ArrayList 的比较
- Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector因为同步操作完全可以由程序员自己来控制 - Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector因为同步操作完全可以由程序员自己来控制
- Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。 - Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。
### 3. 替代方案 ### 3. 替代方案
为了获得线程安全的 ArrayList可以使用 `Collections.synchronizedList();` 得到一个线程安全的 ArrayList。 可以使用 `Collections.synchronizedList();` 得到一个线程安全的 ArrayList。
```java ```java
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
@ -322,7 +323,7 @@ List<String> list = new CopyOnWriteArrayList<>();
写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。 写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。
写操作需要加锁,防止同时并发写入时导致写入数据丢失。 写操作需要加锁,防止并发写入时导致写入数据丢失。
写操作结束之后需要把原始数组指向新的复制数组。 写操作结束之后需要把原始数组指向新的复制数组。
@ -386,9 +387,9 @@ transient Node<E> first;
transient Node<E> last; transient Node<E> last;
``` ```
<div align="center"> <img src="../pics//49495c95-52e5-4c9a-b27b-92cf235ff5ec.png"/> </div><br> <div align="center"> <img src="../pics//49495c95-52e5-4c9a-b27b-92cf235ff5ec.png" width="500"/> </div><br>
### 2. ArrayList 与 LinkedList ### 2. 与 ArrayList 的比较
- ArrayList 基于动态数组实现LinkedList 基于双向链表实现; - ArrayList 基于动态数组实现LinkedList 基于双向链表实现;
- ArrayList 支持随机访问LinkedList 不支持; - ArrayList 支持随机访问LinkedList 不支持;
@ -406,7 +407,7 @@ transient Node<E> last;
transient Entry[] table; transient Entry[] table;
``` ```
其中Entry 就是存储数据的键值对,它包含了四个字段。从 next 字段我们可以看出 Entry 是一个链表,即数组中的每个位置被当成一个桶,一个桶存放一个链表,链表中存放哈希值相同的 Entry也就是说,HashMap 使用拉链法来解决冲突。 Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值相同的 Entry
<div align="center"> <img src="../pics//8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png" width="600"/> </div><br> <div align="center"> <img src="../pics//8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png" width="600"/> </div><br>
@ -634,8 +635,8 @@ y&(x-1) : 00000010
这个性质和 y 对 x 取模效果是一样的: 这个性质和 y 对 x 取模效果是一样的:
``` ```
x : 00010000
y : 10110010 y : 10110010
x : 00010000
y%x : 00000010 y%x : 00000010
``` ```
@ -693,7 +694,7 @@ void addEntry(int hash, K key, V value, int bucketIndex) {
} }
``` ```
扩容使用 resize() 实现,需要注意的是,扩容操作同样需要把旧 table 的所有键值对重新插入新的 table 中,因此这一步是很费时的。 扩容使用 resize() 实现,需要注意的是,扩容操作同样需要把 oldTable 的所有键值对重新插入 newTable 中,因此这一步是很费时的。
```java ```java
void resize(int newCapacity) { void resize(int newCapacity) {
@ -739,7 +740,10 @@ capacity : 00010000
new capacity : 00100000 new capacity : 00100000
``` ```
对于一个 Key它的哈希值如果在第 6 位上为 0那么取模得到的结果和之前一样如果为 1那么得到的结果为原来的结果 +16。 对于一个 Key
- 它的哈希值如果在第 6 位上为 0那么取模得到的结果和之前一样
- 如果为 1那么得到的结果为原来的结果 +16。
### 7. 扩容-计算数组容量 ### 7. 扩容-计算数组容量
@ -778,7 +782,7 @@ static final int tableSizeFor(int cap) {
从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。 从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。
### 9. HashMap 与 HashTable ### 9. 与 HashTable 的比较
- HashTable 使用 synchronized 来进行同步。 - HashTable 使用 synchronized 来进行同步。
- HashMap 可以插入键为 null 的 Entry。 - HashMap 可以插入键为 null 的 Entry。
@ -939,7 +943,7 @@ transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail; transient LinkedHashMap.Entry<K,V> tail;
``` ```
accessOrder 决定了顺序,默认为 false此时使用的是插入顺序。 accessOrder 决定了顺序,默认为 false此时维护的是插入顺序。
```java ```java
final boolean accessOrder; final boolean accessOrder;
@ -1059,14 +1063,14 @@ private static class Entry<K,V> extends WeakReference<Object> implements Map.Ent
### ConcurrentCache ### ConcurrentCache
Tomcat 中的 ConcurrentCache 使用了 WeakHashMap 来实现缓存功能。 Tomcat 中的 ConcurrentCache 使用了 WeakHashMap 来实现缓存功能。
ConcurrentCache 采取的是分代缓存: ConcurrentCache 采取的是分代缓存:
- 经常使用的对象放入 eden 中eden 使用 ConcurrentHashMap 实现,不用担心会被回收(伊甸园); - 经常使用的对象放入 eden 中eden 使用 ConcurrentHashMap 实现,不用担心会被回收(伊甸园);
- 不常用的对象放入 longtermlongterm 使用 WeakHashMap 实现,这些老对象会被垃圾收集器回收。 - 不常用的对象放入 longtermlongterm 使用 WeakHashMap 实现,这些老对象会被垃圾收集器回收。
- 当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,保证频繁被访问的节点不容易被回收。 - 当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,从而保证经常被访问的节点不容易被回收。
- 当调用 put() 方法时,如果缓存当前容量大小超过了 size那么就将 eden 中的所有对象都放入 longterm 中,利用虚拟机回收掉一部分不经常使用的对象。 - 当调用 put() 方法时,如果 eden 的大小超过了 size那么就将 eden 中的所有对象都放入 longterm 中,利用虚拟机回收掉一部分不经常使用的对象。
```java ```java
public final class ConcurrentCache<K, V> { public final class ConcurrentCache<K, V> {