【源码解析】JDK源码之LinkedHashMap
2017-04-11 08:21
441 查看
LinkedHashMap源码,基于jdk1.6.43
他继承了HashMap,并且实现了插入和访问的有序功能
public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>
其也有一个Entry内部类,继承了HashMap的Entry内部类,但是增加了两个属性before、after。源码对这两个属性的注释含义是这两个域包含两个链表用于迭代。
代码中的一个属性header,是双向链表的头指针。由此我们推断,LinkedHashMap的内部实现是在HashMap的基础上增加了一个额外的双向链表,用于保存数据的插入顺序。
布尔类型变量,如果为true的话则按访问顺序排序,如果为false的话则按插入顺序排序。
下面的几个构造方法都直接调用了父类的构造方法,只是调用的参数各不相同,但是共同点是都将accessOrder设置为false。
下面的构造方法有所不同,可以传入accessOrder,即可以在构造时改变accessOrder的值。
下面的init方法覆盖了父类的init方法,我们分析HashMap源码时了解到在构造的最后会调用init方法,默认的init方法实现为空,其目的是交由子类进行各自的处理,其在LinkedHashMap中发挥了重要的作用。下面的代码含义就是在构造完成后,初始化头指针,并根据双向链表的特性将头指针的前后指针都指向自己本身,实现双向链接的初始化操作。
下面是上面的init方法调用的本身Entry内部类的构造器,传入的分别是hash值、key值、value值和HashMap中需要指向数组后继元素的next指针变量。我们看到头指针初始化的hash值为-1作为标志位。
重写了父类的transfer方法,该方法的目的是在Map的数组扩容时,重新指定每一个元素的位置,但是其实现与HashMap不同,HashMap的实现是遍历整个数组,对于每个数组元素的单链表重新计算位置,但是LinkedHashMap并没有直接采用父类的方法,而是遍历了每个元素的前后指针,我们知道链表的遍历速度比数组快一些,所以这种实现是一个很高效率的实现。
下面的方法也覆盖了父类的方法重新实现,目的也是为了更快的实现遍历。
public boolean containsValue(Object value)
调用父类的clear方法,并将头指针的前后指针重置为header。
下面的方法使用protected修饰,明显就是为了让子类进行后续的修改,且目前的实现中一直返回false。
下面的方法比较重要,LinkedHashMap中没有覆盖父类的put方法,但是他是如何来维护链表的顺序呢?关键就是下面的addEntry方法,这个addEntry根据HashMap中的put函数中的代码可知他会在put方法的最后被调用。HashMap的addEntry方法中直接放置新加入元素的位置,然后决定是否扩充数组的大小。但是LinkedHashMap的addEntry方法中,在判断是否扩充数组前调用了createEntry方法,来实现维护链表顺序。这个方法在createEntry之后进行了一步判断removeEldestEntry,从上面的代码可以看到这个方法目前是false,就会走else的分支。if这个分支中的操作其实会删除最先插入的元素。但是实际在LinkedHashMap中并没有使用。
下面就是createEntry方法
这个get方法覆盖了父类的get方法,其目的是为了调用e.recordAccess(this);这一行代码。
上面那一行代码的意义如下,根据accessOrder的设置来修改双向链表的顺序,以实现根据访问来排序双向链表。
还有一个remove方法定义在Entry内部,用于删除元素,LinkedHashMap中没有remove方法,所以remove方法的调用其实是调用父类的方法,但是HashMap的remove方法在结尾调用了Entry的recordRemoval方法,而LinkedHashMap在重写recordRemoval方法时调用了其Entry的remove方法,最终实现了在删除元素时对自身链表的操作,从链表中删除元素并改变指针的指向。
他继承了HashMap,并且实现了插入和访问的有序功能
public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>
其也有一个Entry内部类,继承了HashMap的Entry内部类,但是增加了两个属性before、after。源码对这两个属性的注释含义是这两个域包含两个链表用于迭代。
private static class Entry<K, V> extends HashMap.Entry<K, V> // These fields comprise the doubly linked list used for iteration. Entry<K, V> before, after;
代码中的一个属性header,是双向链表的头指针。由此我们推断,LinkedHashMap的内部实现是在HashMap的基础上增加了一个额外的双向链表,用于保存数据的插入顺序。
private transient Entry<K, V> header;
布尔类型变量,如果为true的话则按访问顺序排序,如果为false的话则按插入顺序排序。
private final boolean accessOrder;
下面的几个构造方法都直接调用了父类的构造方法,只是调用的参数各不相同,但是共同点是都将accessOrder设置为false。
public LinkedHashMap(int initialCapacity, float loadFactor) public LinkedHashMap(int initialCapacity) public LinkedHashMap() public LinkedHashMap(Map<? extends K, ? extends V> m)
下面的构造方法有所不同,可以传入accessOrder,即可以在构造时改变accessOrder的值。
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
下面的init方法覆盖了父类的init方法,我们分析HashMap源码时了解到在构造的最后会调用init方法,默认的init方法实现为空,其目的是交由子类进行各自的处理,其在LinkedHashMap中发挥了重要的作用。下面的代码含义就是在构造完成后,初始化头指针,并根据双向链表的特性将头指针的前后指针都指向自己本身,实现双向链接的初始化操作。
void init() { header = new Entry<K, V>(-1, null, null, null); header.before = header.after = header; }
下面是上面的init方法调用的本身Entry内部类的构造器,传入的分别是hash值、key值、value值和HashMap中需要指向数组后继元素的next指针变量。我们看到头指针初始化的hash值为-1作为标志位。
Entry(int hash, K key, V value, HashMap.Entry<K, V> next) { super(hash, key, value, next); }
重写了父类的transfer方法,该方法的目的是在Map的数组扩容时,重新指定每一个元素的位置,但是其实现与HashMap不同,HashMap的实现是遍历整个数组,对于每个数组元素的单链表重新计算位置,但是LinkedHashMap并没有直接采用父类的方法,而是遍历了每个元素的前后指针,我们知道链表的遍历速度比数组快一些,所以这种实现是一个很高效率的实现。
void transfer(HashMap.Entry[] newTable) { int newCapacity = newTable.length; for (Entry<K, V> e = header.after; e != header; e = e.after) { int index = indexFor(e.hash, newCapacity); e.next = newTable[index]; newTable[index] = e; } }
下面的方法也覆盖了父类的方法重新实现,目的也是为了更快的实现遍历。
public boolean containsValue(Object value)
调用父类的clear方法,并将头指针的前后指针重置为header。
public void clear() { super.clear(); header.before = header.after = header; }
下面的方法使用protected修饰,明显就是为了让子类进行后续的修改,且目前的实现中一直返回false。
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return false; }
下面的方法比较重要,LinkedHashMap中没有覆盖父类的put方法,但是他是如何来维护链表的顺序呢?关键就是下面的addEntry方法,这个addEntry根据HashMap中的put函数中的代码可知他会在put方法的最后被调用。HashMap的addEntry方法中直接放置新加入元素的位置,然后决定是否扩充数组的大小。但是LinkedHashMap的addEntry方法中,在判断是否扩充数组前调用了createEntry方法,来实现维护链表顺序。这个方法在createEntry之后进行了一步判断removeEldestEntry,从上面的代码可以看到这个方法目前是false,就会走else的分支。if这个分支中的操作其实会删除最先插入的元素。但是实际在LinkedHashMap中并没有使用。
void addEntry(int hash, K key, V value, int bucketIndex) { createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate Entry<K, V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } else { if (size >= threshold) resize(2 * table.length); } }
下面就是createEntry方法
void createEntry(int hash, K key, V value, int bucketIndex) { // 根据元素应该放置的位置获得数组该位置的元素 HashMap.Entry<K, V> old = table[bucketIndex]; // 根据获得的元素创建一个Entry对象 Entry<K, V> e = new Entry<K, V>(hash, key, value, old); // 将新创建的对象赋值替代原有的数组元素 table[bucketIndex] = e; // 将这个元素插入到头指针之前 e.addBefore(header); // map的大小加一 size++; }
这个get方法覆盖了父类的get方法,其目的是为了调用e.recordAccess(this);这一行代码。
public V get(Object key) { Entry<K, V> e = (Entry<K, V>) getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; }
上面那一行代码的意义如下,根据accessOrder的设置来修改双向链表的顺序,以实现根据访问来排序双向链表。
void recordAccess(HashMap<K, V> m) { LinkedHashMap<K, V> lm = (LinkedHashMap<K, V>) m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }
还有一个remove方法定义在Entry内部,用于删除元素,LinkedHashMap中没有remove方法,所以remove方法的调用其实是调用父类的方法,但是HashMap的remove方法在结尾调用了Entry的recordRemoval方法,而LinkedHashMap在重写recordRemoval方法时调用了其Entry的remove方法,最终实现了在删除元素时对自身链表的操作,从链表中删除元素并改变指针的指向。
private void remove() { before.after = after; after.before = before; } void recordRemoval(HashMap<K, V> m) { remove(); }
相关文章推荐
- Java集合框架--LinkedList源码解析(JDK1.7)
- JDK1.8源码解析之 HashMap
- java.util.LinkedHashMap源码解析
- JDK源码解析集合篇--HashMap无敌全解析
- LinkedList源码解析——JDK1.8
- LinkedList源码解析 给jdk写注释系列之jdk1.6容器(2)
- 【集合源码】HashMap源码解析(基于JDK 1.8)
- JDK源码解析之LinkedList
- 【JDk源码解析之三】HashMap源码解析
- HashMap源码解析(基于JDK1.7)
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
- JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable
- Java集合框架--HashMap源码解析(JDK1.7)
- 【jdk源码解析二】java.uti.HashMap源码解析
- HashMap、LinkedHash以及TreeMap源码解析
- [数据结构]--jdk1.8中HashMap源码解析
- JDK源码解析之HashMap类
- JDK之LinkedList源码解析
- JDK源码解析之LinkedList
- JDK源码解析之HashMap