SafeIterableMap
LinkedList, which pretends to be a map and supports modifications during iterations. It is NOT thread safe.
- 其底层用时链表(双向)实现,pretends to be (假装) map.
- 支持在迭代过程中直接修改(调用其自身的put 或者remove),比如删除,添加 。其他集合需要通过其Iterator 来实现对集合的修改。否则会抛出
ConcurrentModificationException,虽然Iterator 支持迭代过程中删除,但并不是所有的Iterrator都支持迭代过程中添加,- 线程不安全
- 一般不能直接在项目中使用,Studio 会变红,但是不影响正常编译和使用,可以通过添加注解
@SuppressLint("RestrictedApi")来抑制警告- 在迭代过程中不能像其他集合一样调用
Iterator的remove 方法,其默认实现是抛出UnsupportedOperationException
概述
SafeIterableMap 他是google工程师在 Androidx 中提供的一个简单数据结构,非线程安全,以键值对方式存取数据,看似一个map,其底层是一个双向链表,每一个键值对被包装成一个 Entry<K,V> 被保存。支持双向遍历,并且支持在迭代过程中直接对集合的修改(直接调用map的put 或者remove方法,比如添加删除,不能调用Iteratorr的remove方法)。其他集合在迭代过程中只能通过Iterator进行修改操作。并且不是所有的Iterator 都支持添加,只有List 中的ListIterator 支持添加操作,当然SafeIterableMap 支持迭代过程中支持修改操作,是因为他自己实现的Iterator 支持了, SafeIterableMap 一共有三个 Iterator 实现类,AscendingIterator (顺序迭代),DescendingIterator (反向迭代),IteratorWithAdditions(顺序迭代,支持添加操作),三个都实现了SupportRemove 接口,并且实现了SupportRemove接口中的supportRemove方法,该方法就是用来当 SafeIterableMap 中Entry 被删除时,让Iterator 感知到,使其把对SafeIterableMap 的修改同步到Iterator 中。因此 这三个Iterator 都支持迭代过程中删除,但是只有IteratorWithAdditions支持迭代过程中添加新的。其他两个Iteratro 在迭代的时候调用SafeIterableMap 的put方法不会抛出异常,只是不能迭代到新put的数据而已。
因为我们到知道其他集合不支持在迭代过程中直接调用集合的修改方法对集合进行修改,就是因为集合中有一个变量
modCount记录了集合修改的次数,每修改一次modCount都会加一,迭代集合时Iterator在被创建出来(new)的时记录了集合当前的一个状态,比如集合的修改次数modCount,集合的size等,在调用Iterator方法进行迭代的时候会去判断Iterator中记录的modCount次数是否和集合中最新的modCount是否相等,如果相等意味着集合没有发生修改,如果不相等意味着集合一定发生了修改,直接抛出了ConcurrentModificationException。
File Structure

源码解析
/**
* LinkedList, which pretends to be a map and supports modifications during iterations.
* It is NOT thread safe.
* 一个底层用链表实现的类似与Map的集合,并且支持在迭代过程中对map 进行修改
*/
//这个注解导致了我们在项目代码中使用此api studio 会有警告提示,但不影响编译和正常运行。可以通过注解消除警告
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class SafeIterableMap<K, V> implements Iterable<Map.Entry<K, V>> {
//链表的头结点
Entry<K, V> mStart;
// 链表的尾结点
private Entry<K, V> mEnd;
/**
* using WeakHashMap over List<WeakReference>, so we don't have to manually remove
* WeakReferences that have null in them.
*
* 使用WeakHashMap代替List<WeakReference>,这样就不用再手动删除WeakReferences,
* 意思就是说,如果用List<WeakReference> 存储Iterator,其结构将是这样的List<WeakReference<Iterator>>
* 那么当Iterrator被回收后,还需要手动从List中把WeakReference这个对象删,用WeakHashMap则省掉了这个操作.
*
* 该Map用来保存迭代时new 出来的迭代器,以便在调用 sefaIterableMap.remove()方法时通知到所有迭代器,更新迭代
* 器里面的状态,使迭代器能够响应 map 的修改,比如你在迭代过程中对map删除了某一个key,那么在迭代过程中,将不会获取
* 道这个entry.如果是使用InteratorWithAdditions这个迭代器,在迭代过程中还可以调用map.putIfAbsend方法添加一个
* 新的entry,别切会在迭代过程中会被输出
*/
private WeakHashMap<SupportRemove<K, V>, Boolean> mIterators = new WeakHashMap<>();
// map 中 元素的个数,也即是链表的长度
private int mSize = 0;
/**
* 从头开始遍历链表中的每一个Eetry,如果entry.key equals 传入的key
* 则返回该entry。如果链表为null,或者找不到对应的entry 返回null
*/
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;
}
/**
* 如果不存在则保存,返回null
* 如果已经存在则返回存在的旧值
*/
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;
}
/**
* 把key,value 包装成 Entry 插入到链表尾部,size++
* 并返回新保存entry对象
*
*/
protected Entry<K, V> put(@NonNull K key, @NonNull V v) {
Entry<K, V> newEntry = new Entry<>(key, v);
mSize++;
if (mEnd == null) {
// 表示链表为null,首尾都指向这个newEntry
mStart = newEntry;
mEnd = mStart;
return newEntry;
}
// 更新尾结点,使newEntry为新的尾结点
mEnd.mNext = newEntry;
newEntry.mPrevious = mEnd;
mEnd = newEntry;
return newEntry;
}
/**
* Removes the mapping for a key from this map if it is present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with the specified key,
* or {@code null} if there was no mapping for the key
*
* 从链表中移除一个key对应的entry,如果不存在返回null.如果存在返回这个entry.value
*/
public V remove(@NonNull K key) {
Entry<K, V> toRemove = get(key); //从头开始遍历链表,找到一个与key对应的entry
if (toRemove == null) {
return null; // 如果找不到,返回null
}
mSize--; // 如果找到了,size--
// 遍历还未被回收的iterator,通知其要删除一个entry,
// 这就是safeIterableMap 支持迭代过程中删除的关键代码
if (


774

被折叠的 条评论
为什么被折叠?



