LinkedHashMap类源码解析

LinkedHashMap

1.通过链表实现存储,可以根据插入顺序遍历数组

2.hashcode计算方法和HashMap的一样

继承HashMap

实现Map

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>

成员变量


    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * 双链表的头结点
     */
    private transient Entry<K,V> header;

    /**
     * 遍历的顺序 对于访问顺序,为true;对于插入顺序,则为false(默认)
     * true for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    private final boolean accessOrder;

构造函数


    /**
     * 构造函数
     *
     * @param  initialCapacity 初始容量
     * @param  loadFactor      装载因子
     * @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;
    }

    /**
     * 装载因子0.75的构造函数
     * @param  initialCapacity 初始容量
     * @throws IllegalArgumentException if the initial capacity is negative
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    /**
     * 构造函数
     *default initial capacity (16) and load factor (0.75).
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    /**
     * 构造函数:m集合元素加入到当前map中
     *  default load factor (0.75)
     * @param  m the map whose mappings are to be placed in this map
     * @throws NullPointerException if the specified map is null
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super(m);
        accessOrder = false;
    }

    /**
     * 构造函数
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

结点定义结构

    /**
     * LinkedHashMap entry.结点定义
     */
    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;

        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }

        /**
         * remove
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }

        /**
         * addBefore
         */
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

        void recordRemoval(HashMap<K,V> m) {
            remove();
        }
    }

双链表的初始化


    /**
     * 初始化,可以看出有一个头指针
     */
    @Override
    void init() {
        header = new Entry<>(-1, null, null, null);
        header.before = header.after = header;
    }

    /**
     * Transfers all entries to new table array.  This method is called
     * by superclass resize.  It is overridden for performance, as it is
     * faster to iterate using our linked list.
     */
    @Override
    void transfer(HashMap.Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e = header.after; e != header; e = e.after) {
            if (rehash)
                e.hash = (e.key == null) ? 0 : hash(e.key);
            int index = indexFor(e.hash, newCapacity);
            e.next = newTable[index];
            newTable[index] = e;
        }
    }

containsValue

直接根据双链表进行顺序遍历、

重写了HashMap的containsValue,效率提高


    /**
     * containsValue
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     */
    public boolean containsValue(Object value) {
        // Overridden to take advantage of faster iterator
        if (value==null) {
            for (Entry e = header.after; e != header; e = e.after)
                if (e.value==null)
                    return true;
        } else {
            for (Entry e = header.after; e != header; e = e.after)
                if (value.equals(e.value))
                    return true;
        }
        return false;
    }

没有实现containsKey方法

HashMap中是根据key计算hashcode,找到对应的table[i],在顺序查找,现在这个查找效率比较高,毕竟只遍历了部分数据。

clear

    /**
     * clear
     */
    public void clear() {
        super.clear();
        header.before = header.after = header;
    }

get

getEntry 方法是调用父类,其程序原理和containsKey方法一样,在HashMap中containsKey方法调用的getEntry方法。

    /**
     * V get(Object key)
     */
    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;
    }

LinkedHashMap没有实现put方法,其重写了HashMap.put里面的recordAccess、addEntry方法

HashMap.put

    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key); // 计算hash值
        int i = indexFor(hash, table.length); // 转化为所在数组的下标
        for (Entry<K,V> e = table[i]; 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; // key已经存在,新的value 更新老的value
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i); // 不存在时候  插入该新的节点
        return null;
    }

LinkedHashMap重写recordAccess、addEntry方法


    /**
     * 重写HashMap的方法,
     * This override alters behavior of superclass put method. It causes newly
     * allocated entry to get inserted at the end of the linked list and
     * removes the eldest entry if appropriate.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex); // 父类方法 

        // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }

    /**
     * This override differs from addEntry in that it doesn‘t resize the
     * table or remove the eldest entry.
     */
    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];
        Entry<K,V> e = new Entry<>(hash, key, value, old); // 新结点 e
        table[bucketIndex] = e; // table 中加入结点 e
        e.addBefore(header); // 加入到头部
        size++;
    }

    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
}

可以看出,表面上LinkedHashMap通过双链表作为存储结构,其实双链表只是记录结点的插入顺序,方便了对LinkedHashMap中结点的遍历,其父类的table同样作为存储结构,table的存在是使得相同的hashcode的结点将会存储在同一个table[i]桶内。符合Map的定义,如果没有table数组,直接双链表存储,没有必要计算hashcode,与LinkedList几乎意义一样。 table数组的存在,在get方法和containsKey方法中用到,LinkedHashMap没有重写containsKey方法,上面有讲解。

迭代器

    // 根据插入顺序的迭代器
    private abstract class LinkedHashIterator<T> implements Iterator<T> {
        Entry<K,V> nextEntry    = header.after;
        Entry<K,V> lastReturned = null;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

        public boolean hasNext() {
            return nextEntry != header;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

            LinkedHashMap.this.remove(lastReturned.key);
            lastReturned = null;
            expectedModCount = modCount;
        }

        Entry<K,V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextEntry == header)
                throw new NoSuchElementException();

            Entry<K,V> e = lastReturned = nextEntry;
            nextEntry = e.after;
            return e;
        }
    }
    // KeyIterator
    private class KeyIterator extends LinkedHashIterator<K> {
        public K next() { return nextEntry().getKey(); }
    }
    // ValueIterator
    private class ValueIterator extends LinkedHashIterator<V> {
        public V next() { return nextEntry().value; }
    }
    // EntryIterator
    private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() { return nextEntry(); }
    }

    // These Overrides alter the behavior of superclass view iterator() methods
    Iterator<K> newKeyIterator()   { return new KeyIterator();   }
    Iterator<V> newValueIterator() { return new ValueIterator(); }
    Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
时间: 2025-01-21 20:12:28

LinkedHashMap类源码解析的相关文章

Java集合---Array类源码解析

Java集合---Array类源码解析              ---转自:牛奶.不加糖 一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型:采用改进的归并排序. 1.对于基本类型源码分析如下(以int[]为例): Java对Primitive(int,float等原型数据)数组采用快速排序,对Object对象数组采用归并排序.对这一区别,sun在

java.lang.Void类源码解析_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code null} * @throws SecurityException if the current thread cannot create a * thread in the specified thread grou

Object类源码解析

本文的分析基于JDK 1.8 Java中所有的类都继承自Object类. Object类的源码解析 1.void registerNatives() private static native void registerNatives(); static { registerNatives(); } 1 2 3 4 5 1 2 3 4 5 该方法只是对几个本地方法进行注册(即初始化java方法映射到C的方法).需要注意的是,很多类中都有这个方法,但是执行注册的目标是不同的.System类中也有该

java-AbstractCollection类-源码解析

转载:原文地址 http://www.cnblogs.com/android-blogs/p/5566212.html 一.Collection接口 从<Java集合:整体结构>一文中我们知道所有的List和Set都继承自Collection接口,该接口类提供了集合最基本的方法,虽然List接口和Set等都有一些自己独有的方法,但是基本的操作类似.我们先看下Collection接口提供的方法: 总体上可以将Collection的方法分为以下几大类: 1.增加(add/addAll) 2.删除(

Scroller类源码解析及其应用(一)

滑动是我们在自定义控件时候经常遇见的难听,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动.另外,我还会结合一个简单的例子,来看一下这个类的应用. 要说明Scroller类,我们往往要从另外两个方法说起,一个是ScrollTo(),一个是ScrollBy() 这两个方法我们可以在View的源码看到,我们知道其实每个空间都有滚动条,只是有的我们将它隐藏,所以我们看不见 下面是ScrollTo方法 /** * Set the scrolled

Android中的ViewRootImpl类源码解析

转载请注明出处 http://blog.csdn.net/qianhaifeng2012/article/details/51737370 ViewRoot目前这个类已经没有了,是老版本中的一个类,在Android2.2以后用ViewRootImpl代替ViewRoot,对应于ViewRootImpl.java,他是链接WindowManager和DecorView的纽带,另外View的绘制也是通过ViewRootImpl来完成的. 它的主要作用我的总结为如下: A:链接WindowManage

Java集合---Arrays类源码解析

一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型:采用改进的归并排序. 1.对于基本类型源码分析如下(以int[]为例): Java对Primitive(int,float等原型数据)数组采用快速排序,对Object对象数组采用归并排序.对这一区别,sun在<<The Java Tutorial>>中做出的解释如下: The sort

机器学习:weka中Evaluation类源码解析及输出AUC及交叉验证介绍

在机器学习分类结果的评估中,ROC曲线下的面积AOC是一个非常重要的指标.下面是调用weka类,输出AOC的源码: try { // 1.读入数据集 Instances data = new Instances( new BufferedReader( new FileReader("E:\\Develop/Weka-3-6/data/contact-lenses.arff"))); data.setClassIndex(data.numAttributes() - 1); // 2.

HashSet类源码解析

HashSet特点 1.内部通过HashMap实现set的特征,HashMap的key就是集合的元素,value都设置为null 2.几乎就是HashMap 源码如下 package java.util; public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { static final long serialVersionUID