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而实现更多的功能。