这一篇分析的是 LinkedHashMap的源码,可能有些同学会有些疑惑,HashMap和LinkedHashMap有什么区别呢?
HashMap的集合的底层是一个散列表(数组+链表)+红黑树
LinkedHashMap集合的底层其实也是一个散列表(数组+链表)+红黑树,只不过是链表是双向链表,HashMap的链表时单向链表结构。因为结构的差异,所以,操作会有些不同。
我们先看一下LinkedHashMap定义便知。
/*
LinkedHashMap继承了HashMap
底层还是一个hashMap(数组+链表),只不过这里的链表是双向链表
牛逼,两个类继承,但是有不同,主要不同之处在与节点的结构不同,但是这个节点有所重中之重,就是散列表的一个结构,增删改查,就是操作的节点
将节点本身的操作提取城一个方法,如创建一个节点,设置后一个节点等方法,这样子类也同样具有这样的方法,但是因为节点结构的不同,可以自己实现
这样的方法,就是重写,利用了多态
*/
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
LinkedHashMap的父类是HashMap类,既然是继承,LinkedHashMap已经具有了很多操作方法了。但是因为结构的不同,会稍微有些改动。
我一直在说,底层结构的改变,那么,我们先来看一下,结构是如何改变的?
/*这个类,也继承了HashMap类的内部类的Node
* 这里的class Entry<K,V> extends HashMap.Node<K,V>,所以,class Entry<K,V>具有next字符,在HashMap中next指向的是下一个节点,
* 但是在class Entry<K,V>中,已经有了指向前一个和后一个节点的字段,(指向的是插入时上一个或下一个节点)
next指向的是定位后,下一个节点,同一个桶中,后一个节点
*/
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);//这是父类Node的成员变量
}
}
这张图就是一个散列表(数组+双向链表)
——LinkedHashMap的成员变量
transient LinkedHashMap.Entry<K,V> head;//首
transient LinkedHashMap.Entry<K,V> tail;//尾
/*
访问顺序(access-ordered)
插入顺序(insertion-ordered)
默认是插入顺序的
* @serial
*/
final boolean accessOrder;
——LinkedHashMap的构造方法(其实都是调用了父类的构造方法)
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;//默认是插入顺序
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
//这个方法稍有区别,因为HashMap的这个方法就是调用的自己的方法进行添加
super();
accessOrder = false;
putMapEntries(m, false);
}
//这个构造方法可以设置访问顺序
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
//这个构造方法可以指定遍历顺序
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
——LinkedHashMap的添加操作
1.需要先创建一个节点(双向节点)
/*
创建一个新的节点
*/
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;
}
2.维护这个节点的位置,维护的是插入顺序。
/*
维护刚插入节点的前后节点的关系,只是插入顺序。与在桶中的位置无关
*/
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
3.添加操作,还是调用的是父类的添加操作,与单向链表的插入一样,因为插入顺序的维护,已经在LinkedHashMap维护了。LinkedHashMap中并没有声明一个添加方法,所以并没重写父类的添加方法,使用的依然是继承的是父类的添加方法。
4.我们再看HashMap的源码时,会发现HashMap定义了三个方法,这个三方法都是空的,其他方法也都调用了这三个方法,当时我并不知道是什么意思,现在知道了其实就是让子类对这三个方法进行重写,说白了,就是多态。调用子类方法。
void afterNodeRemoval(Node<K,V> e) { // unlink删除节点e
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
void afterNodeInsertion(boolean evict) { // possibly remove eldest可能删除最老的节点
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
/*
该方法是这个类的方法,是在根据指定的key获得value时,get()方法中会调用该方法
accessOrder为false,是默认排序(插入顺序)
accessOrder=true时,是访问顺序,
该方法会在指定访问顺序时,会被调用
指定顺序了,就按该方法,最常用的将其放在链表的最后,不常用的放在链表的最前~
*/
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;// p=e, b<--p-->a
p.after = null;//因为要将这个节点放在最后,所以将这个节点的after指向null
//先判断先一个元素
if (b == null)
//双向链表,e是第一个节点,那么第一个要放在最后,head肯定要指向e的下一个节点
head = a;//
else
//有节点,e节点不是第一个节点,将e的前一个节点的after指向e节点的后一个节点
b.after = a;
//在判断后一个元素
if (a != null)
//a!=null,e节点放在最后,a前一个元素=b
a.before = b;
else
//如果a=null,b就是最后一个元素
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;//
++modCount;
}
}
——LinkedHashMap的获取操作
/*
根据指定的key值获得value值
*/
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
//证明map中肯定有key值
afterNodeAccess(e);//
return e.value;
}
/**
* 该方法没啥用,返回指定key的value,如果没有,返回设置的值
*/
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return defaultValue;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
——LinkedHashMap的其他操作
/*
* 清除
*/
public void clear() {
super.clear();
head = tail = null;
}
——LinkedHashMap的映射操作
其实都是调用的是使用了父类的成员变量keyset和values,其实还是依靠父类完成的,没啥说的。
就这样吧,写一篇是LinkedHashSet集合,其实也没啥可以讲的,就大概了解下吧。