LinkedHashSet源码分析
一、准备工作
package com.liu.collection;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSet_ {
public static void main(String[] args) {
Set<Object> set = new LinkedHashSet<>();//在这里打断点
for (int i = 0; i < 10; i++) {
set.add(i);
}
}
}
二、源码分析
进入LinkedHashSet构造函数
super()继承关系
/**
* Constructs a new, empty linked hash set with the default initial
* capacity (16) and load factor (0.75).
*/
public LinkedHashSet() {
super(16, .75f, true);
}
进入super()方法
发现LinkedHashSet是由HashSet来实现的
发现HashSet构造函数中调用LinkedHashMap
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
发现LinedHashMap是有底层的
/**
* Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
* with the specified initial capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
进入HashMap方法,发现LinkedHashMap的底层是HashMap
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
跳出HashMap
跳出LinkedHashMap
跳出HashSet
跳出LinkedHashSet
进入add
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
进入putVal方法
newNode(hash, key, value, null)
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//第一次添加时,进行扩容操作 length变为16
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//赋予下标为i的元素值为newNode(hash, key, value, 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;
}
进入newNode方法
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);//
linkNodeLast(p);
return p;
}
LinkedHashMap的静态内部类Entry
继承了HashMap.Node<K,V>类,所以本身为一个结点,新加了属性after和before,可以用来做双向链表的指针
/**
* HashMap.Node subclass for normal LinkedHashMap entries.
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
进入linkNodeLast§方法
用来进行双向链表增加尾节点的操作
/**
* The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
// link at the end of list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
//定义一个last结点,指向tail(初始为空)
LinkedHashMap.Entry<K,V> last = tail;
//tail结点指向p结点
tail = p;
//若last为空,没有节点
if (last == null)
//则p指向头节点
head = p;
else {//若有尾结点
//p的pre指针指向原来双链表的尾结点
p.before = last;
//原双链表的下一节点指向p
last.after = p;
}
}
看图理解
跳出newNode方法,返回p结点
回到putVal方法,将p结点存入下标为i的数组中
三、总结(重点)
LinkedHashSet底层是LinkedHashMap,而LinkedHashMap的底层是HashMap
LinkedHashSet中的结点与结点之间是由双向指针连接的,就是双向链表,底层是有Entry实现的,有两个属性before和after,刚好对应前驱和后继关系
LinkedHashSet添加元素,采用尾插法
LinkedHashSet中的元素与元素之间存在前驱后继关系,所以输出时和输入是的顺序是一致的(根据头节点一直向后遍历)