Skip to content

Commit c198db8

Browse files
committed
📝 Writing docs.
1 parent 0493909 commit c198db8

File tree

2 files changed

+191
-50
lines changed

2 files changed

+191
-50
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
* [x] [List](docs/container/List.md)
3434
* [x] [Map](docs/container/Map.md)
35+
* [x] [Set](docs/container/Set.md)
3536

3637
## [Java 并发](docs/concurrent)
3738

docs/container/Set.md

Lines changed: 190 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,244 @@
11
---
2-
title: 容器之 Set
3-
date: 2018/05/31
2+
title: Java 容器之 Set
3+
date: 2018/06/28
44
categories:
55
- javase
66
tags:
7+
- java
78
- javase
8-
- collection
9+
- container
910
---
1011

11-
# 容器之 Set
12+
# Java 容器之 Set
1213

13-
<!-- TOC -->
14+
<!-- TOC depthFrom:2 depthTo:2 -->
1415

15-
- [HashSet](#hashset)
16-
- [HashSet 要点](#hashset-要点)
17-
- [HashSet 源码](#hashset-源码)
18-
- [TreeSet](#treeset)
19-
- [TreeSet 要点](#treeset-要点)
20-
- [TreeSet 源码](#treeset-源码)
21-
- [EnumSet](#enumset)
22-
- [EnumSet 要点](#enumset-要点)
16+
- [Set 架构](#set-架构)
17+
- [Set 接口](#set-接口)
18+
- [SortedSet 接口](#sortedset-接口)
19+
- [NavigableSet 接口](#navigableset-接口)
20+
- [AbstractSet 抽象类](#abstractset-抽象类)
21+
- [HashSet 类](#hashset-类)
22+
- [TreeSet 类](#treeset-类)
23+
- [LinkedHashSet 类](#linkedhashset-类)
24+
- [EnumSet 类](#enumset-类)
25+
- [资料](#资料)
2326

2427
<!-- /TOC -->
2528

29+
## Set 架构
2630

27-
Set 集合与 Collection 集合基本相同,没有提供任何额外的方法。
31+
<div align="center">
32+
<img src="https://raw.githubusercontent.com/dunwu/javase-notes/master/images/container/Set-diagrams.png" width="400" />
33+
</div>
2834

29-
实际上 Set 就是 Collection,只是行为略有不同:Set 集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个 Set 集合中,则添加操作失败,add() 方法返回 false,且新元素不会被加入。
35+
- Set 继承了 Collection 的接口。实际上 Set 就是 Collection,只是行为略有不同:Set 集合不允许有重复元素。
36+
- SortedSet 继承了 Set 的接口。SortedSet 中的内容是排序的唯一值,排序的方法是通过比较器(Comparator)。
37+
- NavigableSet 继承了 SortedSet 的接口。相比于 NavigableSet 有一系列的导航方法;如"获取大于/等于某值的元素"、“获取小于/等于某值的元素”等等。
38+
- AbstractSet 是一个抽象类,它继承于 AbstractCollection,AbstractCollection 实现了 Set 中的绝大部分函数,为 Set 的实现类提供了便利。
39+
- HashSet 类依赖于 HashMap,它实际上是通过 HashMap 实现的。HashSet 中的元素是无序的。
40+
- TreeSet 类依赖于 TreeMap,它实际上是通过 TreeMap 实现的。TreeSet 中的元素是有序的。
41+
- LinkedHashSet 类具有 HashSet 的查找效率,且内部使用链表维护元素的插入顺序。
42+
- EnumSet 中所有元素都必须是指定枚举类型的枚举值。
3043

31-
* HashSet:基于哈希实现,支持快速查找,但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的;
44+
## Set 接口
3245

33-
* TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN);
46+
Set 接口定义如下:
3447

35-
* LinkedHashSet:具有 HashSet 的查找效率,且内部使用链表维护元素的插入顺序。
48+
```java
49+
public interface Set<E> extends Collection<E> {}
50+
```
3651

37-
## HashSet
52+
Set 继承了 Collection 的接口。实际上,Set 就是 Collection,二者提供的方法完全相同。
3853

39-
### HashSet 要点
54+
## SortedSet 接口
55+
56+
SortedSet 接口定义如下:
4057

41-
HashSet 不保证元素的迭代访问顺序与插入顺序相同;并且元素的顺序随着时间推移可能发生改变。
58+
```java
59+
public interface SortedSet<E> extends Set<E> {}
60+
```
4261

43-
HashSet 允许 null 值的元素。
62+
SortedSet 接口新扩展的方法:
4463

45-
HashSet 的基本操作(添加,删除,包含和大小)提供了恒定的时间性能,假设散列函数在桶之间正确地分散元素。迭代此集合需要的时间与HashSet实例的大小(元素数量)加上支持HashMap实例的“容量”(桶的数量)的总和成正比。因此,如果迭代性能很重要,不要将初始容量设置得太高(或者负载因子太低)是非常重要的。
64+
- comparator - 返回 Comparator
65+
- subSet - 返回指定区间的子集
66+
- headSet - 返回小于指定元素的子集
67+
- tailSet - 返回大于指定元素的子集
68+
- first - 返回第一个元素
69+
- last - 返回最后一个元素
70+
- spliterator
4671

47-
HashSet 不是并发安全的,如果在并发状态下对其做迭代操作,会抛出 ConcurrentModificationException 异常。
72+
## NavigableSet 接口
4873

49-
### HashSet 源码
74+
NavigableSet 接口定义如下:
75+
76+
```java
77+
public interface NavigableSet<E> extends SortedSet<E> {}
78+
```
79+
80+
NavigableSet 接口新扩展的方法:
81+
82+
- lower - 返回小于指定值的元素中最接近的元素
83+
- higher - 返回大于指定值的元素中最接近的元素
84+
- floor - 返回小于或等于指定值的元素中最接近的元素
85+
- ceiling - 返回大于或等于指定值的元素中最接近的元素
86+
- pollFirst - 检索并移除第一个(最小的)元素
87+
- pollLast - 检索并移除最后一个(最大的)元素
88+
- descendingSet - 返回反序排列的 Set
89+
- descendingIterator - 返回反序排列的 Set 的迭代器
90+
- subSet - 返回指定区间的子集
91+
- headSet - 返回小于指定元素的子集
92+
- tailSet - 返回大于指定元素的子集
93+
94+
## AbstractSet 抽象类
95+
96+
AbstractSet 抽象类定义如下:
97+
98+
```
99+
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {}
100+
```
101+
102+
AbstractSet 类提供 Set 接口的骨干实现,以最大限度地减少实现 Set 接口所需的工作。
103+
104+
事实上,主要的实现已经在 AbstractCollection 中完成。
105+
106+
## HashSet 类
107+
108+
HashSet 类定义如下:
50109

51110
```java
52111
public class HashSet<E>
53112
extends AbstractSet<E>
54-
implements Set<E>, Cloneable, java.io.Serializable {
113+
implements Set<E>, Cloneable, java.io.Serializable {}
114+
```
115+
116+
### HashSet 要点
117+
118+
1. HashSet 类通过继承 AbstractSet 实现了 Set 接口中的骨干方法。
119+
2. HashSet 实现了 Cloneable,所以支持克隆。
120+
3. HashSet 实现了 Serializable,所以支持序列化。
121+
4. HashSet 中存储的元素是无序的。
122+
5. HashSet 允许 null 值的元素。
123+
6. HashSet 不是线程安全的。
55124

56-
// HashSet 的核心,通过维护一个 HashMap 实体来实现 HashSet 方法
57-
private transient HashMap<E,Object> map;
125+
### HashSet 原理
58126

59-
// PRESENT 是用于关联 map 中当前操作元素的一个虚拟值
60-
private static final Object PRESENT = new Object();
127+
```java
128+
// HashSet 的核心,通过维护一个 HashMap 实体来实现 HashSet 方法
129+
private transient HashMap<E,Object> map;
130+
131+
// PRESENT 是用于关联 map 中当前操作元素的一个虚拟值
132+
private static final Object PRESENT = new Object();
61133
}
62134
```
63135

64-
HashSet 中维护了一个 HashMap 对象 map,它是 HashSet 的核心。实际上,HashSet 的重要方法,如 add、remove、iterator、clear、size 等都是围绕 map 实现的。所以,可以说,HashSet 的实现本质上依赖于 HashMap。
136+
**HashSet 是基于 HashMap 实现的。**
137+
138+
HashSet 中维护了一个 HashMap 对象 map,HashSet 的重要方法,如 add、remove、iterator、clear、size 等都是围绕 map 实现的。
65139

66140
PRESENT 是用于关联 map 中当前操作元素的一个虚拟值。
67141

68-
## TreeSet
142+
HashSet 类中通过定义 `writeObject()``readObject()` 方法确定了其序列化和反序列化的机制。
69143

70-
### TreeSet 要点
144+
## TreeSet
71145

72-
基于 TreeMap 的 NavigableSet 实现。这些元素使用它们的自然顺序或者在创建集合时提供的比较器进行排序,具体取决于使用哪个构造函数。
146+
TreeSet 类定义如下:
147+
148+
```java
149+
public class TreeSet<E> extends AbstractSet<E>
150+
implements NavigableSet<E>, Cloneable, java.io.Serializable {}
151+
```
73152

74-
TreeSet 会对元素进行排序,排序规则是自然顺序或比较器(Comparator)中提供的顺序规则。
153+
### TreeSet 要点
75154

76-
TreeSet 不是并发安全的。如果在并发环境下使用,需要在外部调用处保证同步。
155+
1. TreeSet 类通过继承 AbstractSet 实现了 NavigableSet 接口中的骨干方法。
156+
2. TreeSet 实现了 Cloneable,所以支持克隆。
157+
3. TreeSet 实现了 Serializable,所以支持序列化。
158+
4. TreeSet 中存储的元素是有序的。排序规则是自然顺序或比较器(Comparator)中提供的顺序规则。
159+
5. TreeSet 不是线程安全的。
77160

78161
### TreeSet 源码
79162

80163
```java
81-
public class TreeSet<E> extends AbstractSet<E>
82-
implements NavigableSet<E>, Cloneable, java.io.Serializable
83-
{
84-
// TreeSet 的核心,通过维护一个 NavigableMap 实体来实现 TreeSet 方法
85-
private transient NavigableMap<E,Object> m;
164+
// TreeSet 的核心,通过维护一个 NavigableMap 实体来实现 TreeSet 方法
165+
private transient NavigableMap<E,Object> m;
86166

87-
// PRESENT 是用于关联 map 中当前操作元素的一个虚拟值
88-
private static final Object PRESENT = new Object();
167+
// PRESENT 是用于关联 map 中当前操作元素的一个虚拟值
168+
private static final Object PRESENT = new Object();
89169
```
90170

91-
## EnumSet
171+
**TreeSet 是基于 TreeMap 实现的。**
92172

93-
### EnumSet 要点
173+
TreeSet 中维护了一个 NavigableMap 对象 map(实际上是一个 TreeMap 实例),TreeSet 的重要方法,如 add、remove、iterator、clear、size 等都是围绕 map 实现的。
174+
175+
PRESENT 是用于关联 map 中当前操作元素的一个虚拟值。TreeSet 中的元素都被当成 TreeMap 的 key 存储,而 value 都填的是 PRESENT。
176+
177+
## LinkedHashSet 类
178+
179+
LinkedHashSet 类定义如下:
180+
181+
```java
182+
public class LinkedHashSet<E>
183+
extends HashSet<E>
184+
implements Set<E>, Cloneable, java.io.Serializable {}
185+
```
186+
187+
### LinkedHashSet 要点
188+
189+
1. LinkedHashSet 类通过继承 HashSet 实现了 Set 接口中的骨干方法。
190+
2. LinkedHashSet 实现了 Cloneable,所以支持克隆。
191+
3. LinkedHashSet 实现了 Serializable,所以支持序列化。
192+
4. LinkedHashSet 中存储的元素是按照插入顺序保存的。
193+
5. LinkedHashSet 不是线程安全的。
94194

95-
EnumSet 是用于枚举类型的 Set 实现。
195+
### LinkedHashSet 原理
96196

97-
EnumSet 不允许加入null元素。EnumSet 中的所有元素必须来自单个枚举类型,该集合类型在创建集合时显式或隐式指定
197+
LinkedHashSet 有三个构造方法,无一例外,都是调用父类 HashSet 的构造方法
98198

99-
EnumSet 没有构造方法,只能通过类中的 static 方法来创建 EnumSet 对象。
199+
```java
200+
public LinkedHashSet(int initialCapacity, float loadFactor) {
201+
super(initialCapacity, loadFactor, true);
202+
}
203+
public LinkedHashSet(int initialCapacity) {
204+
super(initialCapacity, .75f, true);
205+
}
206+
public LinkedHashSet() {
207+
super(16, .75f, true);
208+
}
209+
```
210+
211+
需要强调的是:**LinkedHashSet 构造方法实际上调用的是父类 HashSet 的非 public 构造方法。**
212+
213+
```java
214+
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
215+
map = new LinkedHashMap<>(initialCapacity, loadFactor);
216+
}
217+
```
218+
219+
不同于 HashSet public 构造方法中初始化的 HashMap 实例,这个构造方法中,初始化了 LinkedHashMap 实例。
220+
221+
也就是说,实际上,LinkedHashSet 维护了一个双链表。由双链表的特性可以知道,它是按照元素的插入顺序保存的。所以,这就是 LinkedHashSet 中存储的元素是按照插入顺序保存的原理。
222+
223+
## EnumSet 类
224+
225+
EnumSet 类定义如下:
226+
227+
```java
228+
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
229+
implements Cloneable, java.io.Serializable {}
230+
```
231+
232+
### EnumSet 要点
100233

101-
EnumSet 是有序的。以枚举值在 EnumSet 类中的定义顺序来决定集合元素的顺序。
234+
1. EnumSet 类继承了 AbstractSet,所以有 Set 接口中的骨干方法。
235+
2. EnumSet 实现了 Cloneable,所以支持克隆。
236+
3. EnumSet 实现了 Serializable,所以支持序列化。
237+
4. EnumSet 通过 `<E extends Enum<E>>` 限定了存储元素必须是枚举值。
238+
5. EnumSet 没有构造方法,只能通过类中的 static 方法来创建 EnumSet 对象。
239+
6. EnumSet 是有序的。以枚举值在 EnumSet 类中的定义顺序来决定集合元素的顺序。
240+
7. EnumSet 不是线程安全的。
102241

103-
EnumSet 不是并发安全的。
242+
## 资料
104243

244+
- [Java 编程思想(Thinking in java)](https://item.jd.com/10058164.html)

0 commit comments

Comments
 (0)