Set

Posted by LinYaoTian on 2018-12-09

Set集合不包含重复的元素,这是使用Set的主要原因。有三种常见的Set实现——HashSet, TreeSet和LinkedHashSet。什么时候使用它们,使用哪个是个重要的问题。

总体而言:

  • 如果你需要一个访问快速的Set,你应该使用HashSet;
  • 当你需要一个排序的Set,你应该使用TreeSet;
  • 当你需要记录下插入时的顺序时,你应该使用LinedHashSet

HashSet

HashSet 的实现借助了 HashMap ,所以非常的简短。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 // HashSet 真实的存储元素结构
private transient HashMap<E,Object> map;

// 作为各个存储在 HashMap 元素的键值对中的 Value
private static final Object PRESENT = new Object();

//空参数构造方法 调用 HashMap 的空构造参数
//初始化了 HashMap 中的加载因子 loadFactor = 0.75f
public HashSet() {
map = new HashMap<>();
}

//指定期望容量的构造方法
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
//指定期望容量和加载因子
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
//使用指定的集合填充Set
public HashSet(Collection<? extends E> c) {
//调用 new HashMap<>(initialCapacity) 其中初始期望容量为 16 和 c 容量 / 默认 load factor 后 + 1的较大值
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}

// 该方法为 default 访问权限,不允许使用者直接调用,目的是为了初始化 LinkedHashSet 时使用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

public int size() {
return map.size();
}

public boolean isEmpty() {
return map.isEmpty();
}

public boolean contains(Object o) {
return map.containsKey(o);
}

//调用 remove(Object key) 方法去移除对应的键值对
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}

public void clear() {
map.clear();
}

// 返回一个 map.keySet 的 HashIterator 来作为 Set 的迭代器
public Iterator<E> iterator() {
return map.keySet().iterator();
}

HashSet 在存储时把元素作为HashMapkey (其value 为一个定值 PRESENT

HashSet 的不可重复性是由 HashMapkey 不可重复来实现的。

比较过程:当keyhash 值相等时,再比对 equals() 方法是否相等,若相等,则两个key 相等。

HashSet 添加两个’相等’的元素时,后面的元素会覆盖前面的元素。

LinkedHashSet

继承于 HashSet ,只是多了一个双向链表用来维护元素的添加顺序。

1
2
3
4
5
6
7
8
9
10
11
// dummy 参数没有作用这里可以忽略
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

//调用 LinkedHashMap 的构造方法,该方法初始化了初始起始容量,以及加载因子,
//accessOrder = false 即迭代顺序不等于访问顺序
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}

TreeSet

TreeSet的实现借助了TreeMap,基本所有的方法都是调用TreeMap实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

/**
* Constructs a set backed by the specified navigable map.
*/
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}

public TreeSet() {
this(new TreeMap<E,Object>());
}

public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}

public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}

public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}

public int size() {
return m.size();
}

public boolean isEmpty() {
return m.isEmpty();
}

public boolean contains(Object o) {
return m.containsKey(o);
}

public boolean add(E e) {
return m.put(e, PRESENT)==null;
}

public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}

public void clear() {
m.clear();
}

public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
......
}