这一篇分析的是 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集合,其实也没啥可以讲的,就大概了解下吧。