AAC学习笔记——SafeIterableMap

SafeIterableMap是个链表、假Map,它不是线程安全的
这个类支持升序、降序以及支持迭代期间添的式迭代器
它在androidx.arch.core.internal包,估计很少直接用到


Entry

从SafeIterableMap开始看起:

public class SafeIterableMap<K, V> implements Iterable<Map.Entry<K, V>> {
    //开始的入口
    Entry<K, V> mStart;         //这个链表的开始节点
    private Entry<K, V> mEnd;   //这个链表最后也就是最新的节点
    // 在List<WeakReference>上使用WeakHashMap,这样我们就不必手动删除其中含有null的WeakReferences。
    //这里面记录了调用过的迭代器
    
    //supportRemove字面理解可能是支持删除或删除支持~~
    //我理解为:真正移除节点之前的准备工作,而这个工作是针对迭代器的操作
    private WeakHashMap<SupportRemove<K, V>, Boolean> mIterators = new WeakHashMap<>();
    private int mSize = 0;

和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
引用自:http://www.cnblogs.com/skywang12345/p/3311092.html

和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

从开始的定义发现,首先需要要看一下Entry。

这里说一下Map.Entry。它是MAP里面的一个条目(键值对),可以通过Map.entrySet方法返回它。想要获得对条目的引用,必须使用它的迭代器获得。这个对象仅在迭代期间有效,确切地说,如果在迭代器返回后是无法修改的,必须使用setValue操作。

有的说法是使用它可以返回完整的键值对,但我觉得这种说法并不全面。

    static class Entry<K, V> implements Map.Entry<K, V> {
        final K mKey;
        final V mValue;
        Entry<K, V> mNext;            //指向下一个Entry        
        Entry<K, V> mPrevious;        //指向前一个Entry
        //构造器
        Entry(@NonNull K key, @NonNull V value) {mKey = key; this.mValue = value;}
        
        //都是重写Map.Entry的
        public K getKey() {return mKey;}            
        public V getValue() {return mValue;}

        //在这里使用,不允许修改
        @Override
        public V setValue(V value) {throw new UnsupportedOperationException(...);}

        @Override
        public String toString() {return mKey + "=" + mValue;}

        @SuppressWarnings("ReferenceEquality")
        @Override
        public boolean equals(Object obj) {...}

        //每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值
        //得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置
        @Override
        public int hashCode() {return mKey.hashCode() ^ mValue.hashCode();}
    }

通过上面两段代码,总结来说Entry就是每个独立的节点。

get、put、putIfAbsent

   //链表get的标准写法
   protected Entry<K, V> get(K k) {
        Entry<K, V> currentNode = mStart;
        while (currentNode != null) {
            if (currentNode.mKey.equals(k)) {break;} //找到了就返回
            currentNode = currentNode.mNext;         //如果不是就往下走
        }
        return currentNode;
    }
    
    protected Entry<K, V> put(@NonNull K key, @NonNull V v) {
        Entry<K, V> newEntry = new Entry<>(key, v);
        mSize++;
        if (mEnd == null) {           //如果链表本来就是空的直接增加
            mStart = newEntry;        //SafeIterableMap.mStart
            mEnd = mStart;            //SafeIterableMap.mEnd,指针指向mStart
            return newEntry;
        }

        mEnd.mNext = newEntry;       //之前最后节点的下一个指向新增节点
        newEntry.mPrevious = mEnd;   //将之前最后一个节点作为增加节点的上一节点
        mEnd = newEntry;             //将链表的最后节点指向新节点
        return newEntry;             //返回put的Entry

    }
    
    //首先看看新增的节点是否存在,如果不存在才增加
    public V putIfAbsent(@NonNull K key, @NonNull V v) {
        Entry<K, V> entry = get(key); //找
        if (entry != null) {return entry.mValue;} //找到就返回
        put(key, v);                              //只有不存在时才增加
        return null;
    }


Iterator

    private abstract static class ListIterator<K, V> implements Iterator<Map.Entry<K, V>>,
            SupportRemove<K, V> {
        Entry<K, V> mExpectedEnd;   //预计结束的节点
        Entry<K, V> mNext;          //开始节点

        ListIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {
            this.mExpectedEnd = expectedEnd;
            this.mNext = start;
        }
        @Override
        public boolean hasNext() {return mNext != null;}

        @Override    //真正删除节点前执行的,可能向后让也可能向前让,反正就是让出将要删除的节点
        public void supportRemove(@NonNull Entry<K, V> entry) {    
            if (mExpectedEnd == entry && entry == mNext) {            //就剩自己了,直接干掉
                mNext = null;
                mExpectedEnd = null;
            }

            if (mExpectedEnd == entry) {
                mExpectedEnd = backward(mExpectedEnd);  //传入的Eentry等于预计结束,则退后移动一个节点
            }

            if (mNext == entry) {                       //传入的Eentry等于开始,则往前移动一个节点
                mNext = nextNode();
            }
        }

        @SuppressWarnings("ReferenceEquality")
        private Entry<K, V> nextNode() {                //往前移动的时候得判断一下,如果没有了就返回null
            if (mNext == mExpectedEnd || mExpectedEnd == null) {
                return null;
            }
            return forward(mNext);
        }

        @Override
        public Map.Entry<K, V> next() {
            Map.Entry<K, V> result = mNext;
            mNext = nextNode();
            return result;
        }

        abstract Entry<K, V> forward(Entry<K, V> entry);

        abstract Entry<K, V> backward(Entry<K, V> entry);
    }

    //升序迭代
    static class AscendingIterator<K, V> extends ListIterator<K, V> {
        AscendingIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {super(start, expectedEnd);}

        @Override       //向后移动指针
        Entry<K, V> forward(Entry<K, V> entry) {return entry.mNext;}

        @Override      //向前移动指针
        Entry<K, V> backward(Entry<K, V> entry) {return entry.mPrevious;}
    }

    //降序迭代
    private static class DescendingIterator<K, V> extends ListIterator<K, V> {
        DescendingIterator(Entry<K, V> start, Entry<K, V> expectedEnd) {super(start, expectedEnd);}

        @Override      //向前移动指针
        Entry<K, V> forward(Entry<K, V> entry) {return entry.mPrevious;}

        @Override      //向后移动指针
        Entry<K, V> backward(Entry<K, V> entry) {return entry.mNext;}
    }
    
    //这货在持有迭代器期间仍然可以添加节点,从mStart开始一直到木有……
    //这里有个mBeforeStart的标记,当new完了它是true
    //但你一使用它,首先会把指针指向mStart,然后这个标志就false了。除非你把mStart也干掉了,它变成true
    //好比薛定谔的猫,你打开并观察的那一刻,他会变化成你看见的样子
    private class IteratorWithAdditions implements Iterator<Map.Entry<K, V>>, SupportRemove<K, V> {
        private Entry<K, V> mCurrent;           //当前的节点
        private boolean mBeforeStart = true;    //处于开始之前的状态,一旦开始就为false

        IteratorWithAdditions() {}

        @Override                               //向后退一步,让出将被删除的节点
        public void supportRemove(@NonNull Entry<K, V> entry) {
            if (entry == mCurrent) {
                mCurrent = mCurrent.mPrevious;
                mBeforeStart = mCurrent == null;
            }
        }

        @Override                             //如果还有下一个
        public boolean hasNext() {
            if (mBeforeStart) { return mStart != null;}
            return mCurrent != null && mCurrent.mNext != null;
        }

        @Override
        public Map.Entry<K, V> next() {
            if (mBeforeStart) {           //如果是开始之前状态,直接返回mStart。往后就是一步一步的啦
                mBeforeStart = false;
                mCurrent = mStart;
            } else {
                mCurrent = mCurrent != null ? mCurrent.mNext : null;
            }
            return mCurrent;
        }
    }
   

可以使用三类迭代器:

      public Iterator<Map.Entry<K, V>> iterator() {
        ListIterator<K, V> iterator = new AscendingIterator<>(mStart, mEnd);
        mIterators.put(iterator, false);
        return iterator;
    }

     public Iterator<Map.Entry<K, V>> descendingIterator() {
        DescendingIterator<K, V> iterator = new DescendingIterator<>(mEnd, mStart);
        mIterators.put(iterator, false);
        return iterator;
    }

     public IteratorWithAdditions iteratorWithAdditions() {
        @SuppressWarnings("unchecked")
        IteratorWithAdditions iterator = new IteratorWithAdditions();
        mIterators.put(iterator, false);
        return iterator;
    }    

remove

不光要删掉,而且还要维护好链条

    public V remove(@NonNull K key) {
        Entry<K, V> toRemove = get(key);  //定位到要删除的节点
        if (toRemove == null) { return null;}
        mSize--;
        //首先是要遍历所有迭代器,如果迭代器正指向要删除的(也可能处在头、尾)节点
        //那么supportRemove()会改变指针从而避开,实现迭代器持有期间删除节点。
        if (!mIterators.isEmpty()) {      //如果mIterators不为空,就遍历
            for (SupportRemove<K, V> iter : mIterators.keySet()) {
                iter.supportRemove(toRemove);   //执行各迭代器的supportRemove()方法
            }
        }

        //如果待删除节点有上一节点,那就把上一节点的下一节点指向待删除节点的下一节点
        //如果没有上一节点了,就把待删除节点下一节点变为开始
        if (toRemove.mPrevious != null) {      
            toRemove.mPrevious.mNext = toRemove.mNext;  
        } else {
            mStart = toRemove.mNext;  
        }

        //如果待删除节点有下一节点,那就把下一节点的上一节点指向待删除节点的上一节点
        //如果没有下一节点了,就把待删除节点下一节点变为最后
        if (toRemove.mNext != null) {
            toRemove.mNext.mPrevious = toRemove.mPrevious;
        } else {
            mEnd = toRemove.mPrevious;
        }

        //清理要删除的节点的链接信息,并返回它
        toRemove.mNext = null;
        toRemove.mPrevious = null;
        return toRemove.mValue;
    }

总结一下

这个类开始有这样一段注解:

LinkedList, which pretends to be a map and supports modifications during iterations.
It is NOT thread safe.

1、刚开始我并不理解所谓的迭代期间支持修改。看完代码发现即使持有遍历器期间,更改链表特别是IteratorWithAdditions类型遍历器(添加删除都可以),是完全没问题的。其他两个类型可以支持删除。即使持有期间改变了链表,那么遍历是不受任何影响的。
2、说到这里我们应该明白迭代的意义了,就是mIterators的每一项可以记录链表的头尾。当然了IteratorWithAdditions除外,它只记录了开始而没有结尾。遍历完成后,会被回收。
3、虽然这个类记录迭代器的mIterators是private的并且V并没有被使用。但其实我们完全可以模仿一个,通过加入V而实现更多的功能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值