首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Java 容器源码分析之Map-Set-List(11)

Java 容器源码分析之Map-Set-List(11)

LinkedHashMap 的实现对于 LinkedHashMap 而言,它继承与 HashMap(public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>)、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类 HashMap 相似,它通过重写父类相关的方法,来实现自己的链接列表特性。下面我们来分析 LinkedHashMap 的源代码:
成员变量LinkedHashMap 采用的 hash 算法和 HashMap 相同,但是它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。看源代码:
[url=][/url]
/*** The iteration ordering method for this linked hash map: <tt>true</tt>* for access-order, <tt>false</tt> for insertion-order.* 如果为true,则按照访问顺序;如果为false,则按照插入顺序。*/private final boolean accessOrder;/*** 双向链表的表头元素。 */private transient Entry<K,V> header;/*** LinkedHashMap的Entry元素。* 继承HashMap的Entry元素,又保存了其上一个元素before和下一个元素after的引用。 */private static class Entry<K,V> extends HashMap.Entry<K,V> {    Entry<K,V> before, after;    ……}[url=][/url]

LinkedHashMap 中的 Entry 集成与 HashMap 的 Entry,但是其增加了 before 和 after 的引用,指的是上一个元素和下一个元素的引用。初始化通过源代码可以看出,在 LinkedHashMap 的构造方法中,实际调用了父类 HashMap 的相关构造方法来构造一个底层存放的 table 数组,但额外可以增加 accessOrder 这个参数,如果不设置,默认为 false,代表按照插入顺序进行迭代;当然可以显式设置为 true,代表以访问顺序进行迭代。如:
public LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder) {    super(initialCapacity, loadFactor);    this.accessOrder = accessOrder;}
我们已经知道 LinkedHashMap 的 Entry 元素继承 HashMap 的 Entry,提供了双向链表的功能。在上述 HashMap 的构造器中,最后会调用 init() 方法,进行相关的初始化,这个方法在 HashMap 的实现中并无意义,只是提供给子类实现相关的初始化调用。
但在 LinkedHashMap 重写了 init() 方法,在调用父类的构造方法完成构造后,进一步实现了对其元素 Entry 的初始化操作。
[url=][/url]
/*** Called by superclass constructors and pseudoconstructors (clone,* readObject) before any entries are inserted into the map.  Initializes* the chain.*/@Overridevoid init() {  header = new Entry<>(-1, null, null, null);  header.before = header.after = header;}[url=][/url]

存储LinkedHashMap 并未重写父类 HashMap 的 put 方法,而是重写了父类 HashMap 的 put 方法调用的子方法void recordAccess(HashMap m) ,void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。我们在之前的文章中已经讲解了HashMap的put方法,我们在这里重新贴一下 HashMap 的 put 方法的源代码:
HashMap.put:
[url=][/url]
public V put(K key, V value) {        if (key == null)            return putForNullKey(value);        int hash = hash(key);        int i = indexFor(hash, table.length);        for (Entry<K,V> e = table; e != null; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(hash, key, value, i);        return null;}[url=][/url]

重写方法:
[url=][/url]
void recordAccess(HashMap<K,V> m) {    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;    if (lm.accessOrder) {        lm.modCount++;        remove();        addBefore(lm.header);        }}void addEntry(int hash, K key, V value, int bucketIndex) {    // 调用create方法,将新元素以双向链表的的形式加入到映射中。    createEntry(hash, key, value, bucketIndex);    // 删除最近最少使用元素的策略定义    Entry<K,V> eldest = header.after;    if (removeEldestEntry(eldest)) {        removeEntryForKey(eldest.key);    } else {        if (size >= threshold)            resize(2 * table.length);    }}void createEntry(int hash, K key, V value, int bucketIndex) {    HashMap.Entry<K,V> old = table[bucketIndex];    Entry<K,V> e = new Entry<K,V>(hash, key, value, old);    table[bucketIndex] = e;    // 调用元素的addBrefore方法,将元素加入到哈希、双向链接列表。      e.addBefore(header);    size++;}private void addBefore(Entry<K,V> existingEntry) {    after  = existingEntry;    before = existingEntry.before;    before.after = this;    after.before = this;
}
返回列表