1、构造函数
TreeSet的无参构造函数如下
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
也就是说底层存储数据的是一个TreeMap,TreeMap是由红黑树实现的。
HashSet的无参构造函数如下
public HashSet() {
map = new HashMap<>();
}
也就是说底层存储数据的是一个HashMap,由Node<K,V>数组实现。
2、add方法
TreeSet的add方法如下
private static final Object PRESENT = new Object();
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
可以看到是在TreeMap中插入一个key为add对象,value为一个Object的键值对,所有的TreeSet中的TreeMap的value都是同一个Object。
TreeMap的put方法:
public V put(K key, V value) {
Entry<K,V> t = root;
//如果原来的TreeMap为空,
if (t == null) {
compare(key, key); // 构造函数中没有初始化comparator的,key为null时会抛异常;否则要看comparator的实现
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
//构造函数中初始化了comparator的,要看具体实现来看能不能用null为键
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
//构造函数中没有初始化comparator的,如果put的键是null会报错
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
按照二叉查找树的搜索方法将新键值对插入到合适的位置,成为红黑树的一个叶节点。
最后调用fixAfterInsertion
方法来将红黑树重新平衡,具体方法就是变色和旋转,简单的说明点此。
HashSet的add方法如下
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
可以看到是在HashMap中插入一个key为add对象,value为一个Object的键值对,同样的所有的HashSet中的HashMap的value都是同一个Object。
HashMap的put方法如下
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) {
// existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
可以看到在插入第一个元素之前HashMap的table属性是null,因此我们会调用resize
方法来对Map进行初始化。
final Node<K,V>