LinkedHashMap

在安卓中Lru缓存机制都是通过LinkedHashMap来实现的,那么我之前的疑问就是当我们对该集合进行迭代的时候,到底是怎么保证每次next()的时候返回给我们的都是我们之前插入的顺序的呢?
这里写图片描述
首先还是得看一看最经典的哈希表的实现方法,即数组+链表模式。
那么我们先看看HashMap是怎么进行值的保存和迭代的。
保存
上图左边的数组中存放的是Entry对象
摘自HashMap源码

static class HashMapEntry<K, V> implements Entry<K, V> {
        final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

假设我们往HashMap存放了String
map.put(“str”,new String(“Hello”));
进入put方法中我们发现,我们是对key进行一个哈希值的计算,然后new出一个Enry,添加到数组中。那么在添加的时候可能会出现这样的情况,即两个key不一样,但他的哈希值却一样,那么这时我们首先根据他的哈希值确定他在数组中的位置(下标)。那个位置如果有了一个Entry,那么我们就以链表的形式将他们串接起来。
迭代
总的来说就是纵向迭代数组,横向迭代链表
举个栗子:
这里写图片描述
我的添加顺序是 001,004,006,010
那么我迭代出来的打印顺序是 001,006,004,010
这个看起来应该很明朗了,001和006的哈希值相同,004和010的哈希值相同。

HashMapEntry<K, V> nextEntry() {
            //...
            while (next == null && nextIndex < tab.length) {
                next = tab[nextIndex++];
            }
            nextEntry = next;

这是一个大致的代码,他的意思就是先把链表迭代完,然后跳到数组的下一个index.
好吧,也该步入正题了。
对于LinkedHashMap,就只有一个困惑,就是他是怎么保证迭代的有序性的。

 static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
        LinkedEntry<K, V> nxt;
        LinkedEntry<K, V> prv;

看一看这个加强版的Entry,多个两个成员变量。
有一个很重要的概念,上面我们提到的数组,他实际上保存的是对象的引用,上面我们迭代时的无序性是因为我们将对象引用放进去时其下标是不确定的。
那么对象实体还是在堆中的,那么我们来看一看LinkedHashMap他是如何让放进去的顺序和拿到的顺序对应起来的。

 @Override void addNewEntry(K key, V value, int hash, int index) {
        // Create new entry, link it on to list, and put it into table
        LinkedEntry<K, V> oldTail = header.prv;
        LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
                key, value, hash, table[index], header, oldTail);
        table[index] = oldTail.nxt = header.prv = newTail;

先看看它是怎么添加一个新的Entry的。
这里写图片描述
他可以按照你的添加顺序将Entry串接起来。
再看看他是怎么迭代的。

 private abstract class LinkedHashIterator<T> implements Iterator<T> {
        LinkedEntry<K, V> next = header.nxt;
        LinkedEntry<K, V> lastReturned = null;
        int expectedModCount = modCount;

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

        final LinkedEntry<K, V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            LinkedEntry<K, V> e = next;
            if (e == header)
                throw new NoSuchElementException();
            next = e.nxt;
            return lastReturned = e;
        }

此时我们使用的是LinkEntry真的pre和next,和之前的数组没有半毛钱关系,他每次next是按照链表的顺序,而不是之前数组的索引。
所以,我们可以按照之前添加的顺序将Entry迭代出来。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值