LinkedHashMap源码

1、分析LinkedHashMap

它是HashMap的子类,可以保持元素按插入或访问有序,这与TreeMap按键排序不同

LinkedHashMap是HashMap的子类,但内部还有一个双向链表维护键值对的顺序,对每个键值对即位于哈希表中,也位于这个双向链表中。

LinkedHashMap支持两种顺序:一种是插入顺序;另外一种是访问顺序

按插入顺序

先添加的在前面;后添加的在跟后面,修改操作不影响顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6D9SIfHa-1582615745209)(images/15.png)]

按访问顺序

最末尾的是最近访问的,最开始的最久没被访问

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RO8bp3De-1582615745211)(images/16.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lSgb1J7s-1582615745211)(images/17.png)]

2、实现原理(基于JDK1.7)

2.1、继承体系

//LinkedHashMap是HashMap的子类
//但内部还有一个双向链表维持键值对的顺序:每个键即位于哈希表中,也位于双向链表中
//LinkedHashMap支持两种顺序:一种是插入顺序;另外一种是访问顺序
public class LinkedHashMap<K,V>
        extends HashMap<K,V>
        implements Map<K,V>

LinkedhashM,ap继承了HashMap,在HashMap的基础上对部分方法进行重写与加入自身的一些特性

需要结合HashMap中的方法进行分析

2.2、内部组成

 //表示双向链表的头,它的类型是Entry的内部类
    private transient Entry<K,V> header;

 //表示双向链表的头,它的类型是Entry的内部类
    private transient Entry<K,V> header;

同时在内部有一个Entrt类,这个类是HashMap.Entry的子类,增嫁了两个变量Before和`AFTER·,指向链表中的前驱和后继

HashMap中的Entry类

//HashMap中的Entry类
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;

    	//注意这个方法在这个类为空方法,需要重写
        void recordAccess(HashMap<K,V> m) {
        }

         	//注意这个方法在这个类为空方法,需要重写
        void recordRemoval(HashMap<K,V> m) {
        }
    }

LinkedHashMap中的Entry类

//相当于节点是链表维护了前向与后向的节点
//维护了插入的顺序
//
private static class Entry<K,V> extends HashMap.Entry<K,V> {

        //增加了两个变量:before和after
        Entry<K,V> before, after;

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

        /**
         * Removes this entry from the linked list.
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }

        /**
         * Inserts this entry before the specified existing entry in the list.
         */
        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.
         */
        //record和recordacess是HashMap.Entry中定义的方法
        //在HashMap中这两个方法实现为空
        //如果是按访问顺序,则将还节点移到链表的尾部
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            //如果按照访问有序,recordAceess调整节点到链表末尾
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

        //将该节点从链表中删除
        void recordRemoval(HashMap<K,V> m) {
            remove();
        }
    }

2.3、构造方法

HashMap中构造方法中有一个init方法,其为空函数。

HashMap中的构造函数j举例:

 public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        init();
    } public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
     //HashMap中init()函数为空
        init();
    }

void init() {
    }




在LinkedHashMap中重写了init方法,用于初始化链表的头节点

//LinkedHashMap重写的init方法
 //在HashMap的构造方法中,会调用init方法,init方法在HashMap的实现中为空
    //也就是被设计用来重写的,
    //在LinkedHashSet中用于初始化链表的头节点
    @Override
    void init() {
        //header被初始化为一个Entry对象
        header = new Entry<>(-1, null, null, null);
        //前驱和后继都指向自己
        header.before = header.after = header;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nmkAXRkW-1582615745212)(images/18.png)]

//LinkedHashMap构造函数举例
 public LinkedHashMap(int initialCapacity, float loadFactor) {
     //调用HashMap中的方法
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

  public LinkedHashMap() {
        super();
        accessOrder = false;
    }

 public LinkedHashMap() {
        super();
        accessOrder = false;
    }

  public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

2.4、put方法

LinkedHashMap是继承自hASHmAP的,它没有对其进行重写但是对其中调用的一些方法进行了重写,从而实现了功能的增加。

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);
        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;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
    //添加节点
        addEntry(hash, key, value, i);
        return null;
    }


 void addEntry(int hash, K key, V value, int bucketIndex) {
     //考虑扩容
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

//添加节点
void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

LinkedHashMap中的放

LinkedHashMap重写了createEntrtaddEntry方法,从而实现了填上附加的一些功能。

重写的addEntry方法

   //如果是新建则会调用addEntry方法添加节点
    void addEntry(int hash, K key, V value, int bucketIndex) {
        
        //调用父类的addEntry;父类的addEntry会调用createEntry创建爱你节点,,
        //LinkedHashMap重写了createEntry方
        super.addEntry(hash, key, value, bucketIndex);

        
        //添加完成后,调用removeEldestEntrt检查是否应该删除老街店
        Entry<K,V> eldest = header.after;
        //如果返回值为true,则调用removeEntryForKey(eldest.key);

        if (removeEldestEntry(eldest)) {
            //删除节点时会调用recordRemoval方法,该方法被LinkedHashMap.Entry重写了,会将节点从链表中删除
            removeEntryForKey(eldest.key);
        }
    }


//重写的createEntry方法
 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);
            //新建节点,并加入到哈希表中,同时加入链表中
        table[bucketIndex] = e;
               //新建节点,并加入到哈希表中,同时加入链表中
        e.addBefore(header);
        size++;
    }



  /**
         * Inserts this entry before the specified existing entry in the list.
         */
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u4NSRKld-1582615745213)(images/19.png)]

2.5、get方法

HashMap中的实现

//HashMap中的get方法
public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

LinkedHashMap中的时效内

    //与HashMap相比,主要是调用了节点的recordAcess方法,如果按访问有序,
    //recordAceess调整该节点到末尾末尾
    public V get(Object key) {
        //与hashMap一致
        Entry<K,V> e = (Entry<K,V>)getEntry(key);
        if (e == null)
            return null;
        //recordAceess调整该节点到链表末尾
        e.recordAccess(this);
        return e.value;
    }


 //record和recordacess是HashMap.Entry中定义的方法
        //在HashMap中这两个方法实现为空
        //如果是按访问顺序,则将还节点移到链表的尾部
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            //如果按照访问有序,recordAceess调整节点到链表末尾
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }


2.6、containsValue

//HashMap中的方法:复杂度O(N^2)
 public boolean containsValue(Object value) {
        if (value == null)
            return containsNullValue();

        Entry[] tab = table;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }


//LinkedHashSet中的犯法  复杂度O(N)
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;
    }

3、小结

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0VHI59e-1582615745214)(images/20.png)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值