Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wushizhenking
CS-Notes
提交
ddb9e862
C
CS-Notes
项目概览
wushizhenking
/
CS-Notes
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
CS-Notes
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
ddb9e862
编写于
8月 13, 2018
作者:
C
CyC2018
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
auto commit
上级
7746f5d7
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
26 addition
and
22 deletion
+26
-22
notes/Java 容器.md
notes/Java 容器.md
+26
-22
未找到文件。
notes/Java 容器.md
浏览文件 @
ddb9e862
...
...
@@ -21,7 +21,7 @@
# 一、概览
容器主要包括 Collection 和 Map 两种,Collection
又包含了 List、Set 以及 Queue
。
容器主要包括 Collection 和 Map 两种,Collection
存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表
。
## Collection
...
...
@@ -29,9 +29,9 @@
### 1. Set
-
HashSet:基于哈希表实现,支持快速查找。但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的
。
-
TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)
。
-
TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN)
。
-
HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的
。
-
LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
...
...
@@ -53,13 +53,14 @@
<div
align=
"center"
>
<img
src=
"../pics//SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png"
/>
</div><br>
-
HashMap:基于哈希表实现;
-
TreeMap:基于红黑树实现。
-
HashMap:基于哈希表实现。
-
HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
-
LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
-
TreeMap:基于红黑树实现。
# 二、容器中的设计模式
...
...
@@ -296,14 +297,14 @@ public synchronized E get(int index) {
}
```
### 2. 与 ArrayList 的
区别
### 2. 与 ArrayList 的
比较
-
Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
-
Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。
### 3. 替代方案
为了获得线程安全的 ArrayList,
可以使用
`Collections.synchronizedList();`
得到一个线程安全的 ArrayList。
可以使用
`Collections.synchronizedList();`
得到一个线程安全的 ArrayList。
```
java
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
;
```
<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 不支持;
...
...
@@ -406,7 +407,7 @@ transient Node<E> last;
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>
...
...
@@ -634,8 +635,8 @@ y&(x-1) : 00000010
这个性质和 y 对 x 取模效果是一样的:
```
x : 00010000
y : 10110010
x : 00010000
y%x : 00000010
```
...
...
@@ -693,7 +694,7 @@ void addEntry(int hash, K key, V value, int bucketIndex) {
}
```
扩容使用 resize() 实现,需要注意的是,扩容操作同样需要把
旧 table 的所有键值对重新插入新的 t
able 中,因此这一步是很费时的。
扩容使用 resize() 实现,需要注意的是,扩容操作同样需要把
oldTable 的所有键值对重新插入 newT
able 中,因此这一步是很费时的。
```
java
void
resize
(
int
newCapacity
)
{
...
...
@@ -739,7 +740,10 @@ capacity : 00010000
new capacity : 00100000
```
对于一个 Key,它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;如果为 1,那么得到的结果为原来的结果 +16。
对于一个 Key,
-
它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;
-
如果为 1,那么得到的结果为原来的结果 +16。
### 7. 扩容-计算数组容量
...
...
@@ -778,7 +782,7 @@ static final int tableSizeFor(int cap) {
从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。
### 9.
HashMap 与 HashTable
### 9.
与 HashTable 的比较
-
HashTable 使用 synchronized 来进行同步。
-
HashMap 可以插入键为 null 的 Entry。
...
...
@@ -939,7 +943,7 @@ transient LinkedHashMap.Entry<K,V> head;
transient
LinkedHashMap
.
Entry
<
K
,
V
>
tail
;
```
accessOrder 决定了顺序,默认为 false,此时
使用
的是插入顺序。
accessOrder 决定了顺序,默认为 false,此时
维护
的是插入顺序。
```
java
final
boolean
accessOrder
;
...
...
@@ -954,7 +958,7 @@ void afterNodeInsertion(boolean evict) { }
### afterNodeAccess()
当一个节点被访问时,如果 accessOrder 为 true,则会将
该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。
当一个节点被访问时,如果 accessOrder 为 true,则会将该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。
```
java
void
afterNodeAccess
(
Node
<
K
,
V
>
e
)
{
// move node to last
...
...
@@ -1003,8 +1007,8 @@ removeEldestEntry() 默认为 false,如果需要让它为 true,需要继承
```
java
protected
boolean
removeEldestEntry
(
Map
.
Entry
<
K
,
V
>
eldest
)
{
return
false
;
}
return
false
;
}
```
### LRU 缓存
...
...
@@ -1059,14 +1063,14 @@ private static class Entry<K,V> extends WeakReference<Object> implements Map.Ent
### ConcurrentCache
Tomcat 中的 ConcurrentCache
就
使用了 WeakHashMap 来实现缓存功能。
Tomcat 中的 ConcurrentCache 使用了 WeakHashMap 来实现缓存功能。
ConcurrentCache 采取的是分代缓存:
-
经常使用的对象放入 eden 中,eden 使用 ConcurrentHashMap 实现,不用担心会被回收(伊甸园);
-
不常用的对象放入 longterm,longterm 使用 WeakHashMap 实现,这些老对象会被垃圾收集器回收。
-
当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,
保证频繁
被访问的节点不容易被回收。
-
当调用 put() 方法时,如果
缓存当前容量
大小超过了 size,那么就将 eden 中的所有对象都放入 longterm 中,利用虚拟机回收掉一部分不经常使用的对象。
-
当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,
从而保证经常
被访问的节点不容易被回收。
-
当调用 put() 方法时,如果
eden 的
大小超过了 size,那么就将 eden 中的所有对象都放入 longterm 中,利用虚拟机回收掉一部分不经常使用的对象。
```
java
public
final
class
ConcurrentCache
<
K
,
V
>
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录