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
被保存。支持双向遍历,并且支持在迭代过程中直接对集合的修改(直接调用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
。
/**
* 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, so we don't have to manually remove
* WeakReferences that have null in them.
*
* 使用WeakHashMap代替List,这样就不用再手动删除WeakReferences,
* 意思就是说,如果用List 存储Iterator,其结构将是这样的List>
* 那么当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 (!mIterators.isEmpty()) {
for (SupportRemove<K, V> iter : mIterators.keySet()) {
// 把要删除的这个entry 通知到迭代器,使迭代器能能响应map的改变
// 具体如果改变的,在下面迭代器的supportRemove方法中
iter.supportRemove(toRemove);
}
}
// 如果要删除的这个节点指向的上一个节点不为空,让上一个节点的next 指向要删除的这个节点的下一个
if (toRemove.mPrevious != null) {
toRemove.mPrevious.mNext = toRemove.mNext;
} else {
// 如果如果要删除的这个节点指向的上一个节点为空,那么说明要删除的这个节点为首首节点
// 那么让要删除的这个节点的下一个节点变为首节点
mStart = toRemove.mNext;
}
// 如果要删除的这个节点的下一个节点不为null.那么让其下一个节点指向要删除的这个节点的上一个节点
if (toRemove.mNext != null) {
toRemove.mNext.mPrevious = toRemove.mPrevious;
} else {
// 如果要删除的这个节点的下一个节点为null,说明要删除的这个节点就是最后一个节点,
// 那么让要删除的这个节点的上一个变成尾结点
mEnd = toRemove.mPrevious;
}
// 把要删除的这个节点的上一个,下一个节点都变为null.即这个节点最终不指向任何节点,也没有
// 任何节点执行它,最终变为一个不可引用的节点,被垃圾回收器回收
toRemove.mNext = null;
toRemove.mPrevious = null;
return toRemove.mValue;// 返回被删除这个节点的value
}
/**
* @return the number of elements in this map
*/
public int size() {
return mSize;
}
/**
* @return an ascending iterator, which doesn't include new elements added during an
* iteration.
* 返回一个顺序遍历的迭代器,并加入到WeakHashMap中,以便remove方法调用的时候能通知到Iterator做出响应
*/
@NonNull
@Override
public Iterator<Map.Entry<K, V>> iterator() {
ListIterator<K, V> iterator = new AscendingIterator<>(mStart, mEnd);
mIterators.put(iterator, false);
return iterator;
}
/**
* @return an descending iterator, which doesn't include new elements added during an
* iteration.
* 返回一个倒序遍历的迭代器,并加入到WeakHashMap中,以便remove方法调用的时候能通知到Iterator做出响应
*/
public Iterator<Map.Entry<K, V>> descendingIterator() {
DescendingIterator<K, V> iterator = new DescendingIterator<>(mEnd, mStart);
mIterators.put(iterator, false);
return iterator;
}
/**
* return an iterator with additions.
* 返回一个顺序遍历,并支持在迭代过程执行putIfAbend操作,并加入到WeakHashMap中,以便remove方法调用的时候能通知到 * Iterator做出响应
*/
public IteratorWithAdditions iteratorWithAdditions() {
@SuppressWarnings("unchecked")
IteratorWithAdditions iterator = new IteratorWithAdditions();
mIterators.put(iterator, false); // false 是一个站位,没有任何意义
return iterator;
}
/**
* @return eldest added entry or null
* 返回头节点
*/
public Map.Entry<K, V> eldest() {
return mStart;
}
/**
* @return newest added entry or null
* 返回尾节点
*/
public Map.Entry<K, V> newest() {
return mEnd;
}
// 只有当两个map 的size 一样,并且 里面的每一个entry 都要相等(即entry 的key 和value 都要相等)
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof SafeIterableMap)) {
return false;
}
SafeIterableMap map = (SafeIterableMap) obj;
if (this.size() != map.size()) {
return false;
}
Iterator<Map.Entry<K, V>> iterator1 = iterator();
Iterator iterator2 = map.iterator();
while (iterator1.hasNext() && iterator2.hasNext()) {
Map.Entry<K, V> next1 = iterator1.next();
Object next2 = iterator2.next();
if ((next1 == null && next2 != null)
|| (next1 != null && !next1.equals(next2))) {
return false;
}
}
return !iterator1.hasNext() && !iterator2.hasNext();
}
@Override
public int hashCode() {
int h = 0;
Iterator<Map.Entry<K, V>> i = iterator();
while (i.hasNext()) {
h += i.next().hashCode();
}
return h;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[");
Iterator<Map.Entry<K, V>> iterator = iterator();
while (iterator.hasNext()) {
builder.append(iterator.next().toString());
if (iterator.hasNext()) {
builder.append(", ");
}
}
builder.append("]");
return builder.toString();
}
// 实现了Iterator接口的一个基类,供AscendingIterator 和 DescendingIterator 继承,
// 实现了SupportRemove 接口,响应map 的remove 操作
private abstract static class ListIterator<K, V> implements Iterator<Map.Entry<K, V>>,
SupportRemove<K, V> {
//迭代时期望的结束节点。因为在迭代过程中有可能发生remove操作,因此这个值随时有可能发生改变,重新指向新的节点
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;
}
/**
* 响应map的remove操作。
*/
@SuppressWarnings("ReferenceEquality")
@Override
public void supportRemove(@NonNull Entry<K, V> entry) {
// 如果要删除的这个节点即是头结点又是尾结点,意味着链表只有一个节点,
// 把下一个节点和期待的尾结点都置为null.这样迭代就不能继续下去了
if (mExpectedEnd == entry && entry == mNext) {
mNext = null;
mExpectedEnd = null;
}
// 如果要删除的这个节点是当前map的尾结点。那么就把遍历时预期的结束节点在原来的基础上往回退一个
if (mExpectedEnd == entry) {
mExpectedEnd = backward(mExpectedEnd);
}
// 如果要删除的这个节点是即将要到来的下一个节点。那么就把指示器指向再下一个节点,跳过将要删除的这个节点
if (mNext == entry) {
mNext = nextNode();
}
}
//返回下一个及节点,如果下一个节点是结束节点,或者结束节点为null,返回null
//结束节点只有在map为空的时候才会变为空。
@SuppressWarnings("ReferenceEquality")
private Entry<K, V> nextNode() {
if (mNext == mExpectedEnd || mExpectedEnd == null) {
return null;
}
// 否则找到mNext指向节点的下一个节点
return forward(mNext);
}
// 返回指示器指向的节点,并且把指示器继续指向下一个,前进一步
@Override
public Map.Entry<K, V> next() {
Map.Entry<K, V> result = mNext;
mNext = nextNode();
return result;
}
// 交由子类实现,找到entry的下一个,顺序迭代器的下一个那就是向右移动,反向迭代器的下一个就是向左
abstract Entry<K, V> forward(Entry<K, V> entry);
// 交由子类实现,找到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);
}
// entry 的下一个,往右
@Override
Entry<K, V> forward(Entry<K, V> entry) {
return entry.mNext;
}
// entry 的上一个,往左
@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);
}
// entry 的下一个,往左
@Override
Entry<K, V> forward(Entry<K, V> entry) {
return entry.mPrevious;
}
// entry 的上一个,往右
@Override
Entry<K, V> backward(Entry<K, V> entry) {
return entry.mNext;
}
}
/**
* 支持在迭代过程中执行除removede 其他操作,也就是put操作
* 他是如何实现的呢?
* 1. 相比其他两个迭代器,他是一个非静态的内部类,因此他能访问map的成员,不需要再创建的时候保存记录迭代的下一个和
* 期望的结束节点
* 2.因为他不相比其他连个迭代器来说不需要提前指向下一次需要迭代的entry,说简单点就是他不关心后续链表的情况。
* 不管链表发生什么样的改变,是删除了某一个还是添加了新的,对他后续的迭代都没有影响,他值需要记住迭代是否开始,以及
* 迭代的当前元素,每一次迭代开始之前,只需要判断上一次next返回的enetry(用mCurrent记录着的)是否有下一个,有就返 * 回mCurrent的下一个,所以在迭代过程中对链表添加或者删除都对齐没有任何影响,最终直到没有下一个结束本次迭代
*
*/
private class IteratorWithAdditions implements Iterator<Map.Entry<K, V>>, SupportRemove<K, V> {
//也是一个指示器指向当前正在迭代的节点,也就是next 方法要返回的节点,迭代还未开始时为空,
//迭代开始后,指向next方法返回出去的那个节点
private Entry<K, V> mCurrent;
//表示开始之前,如果还没开始迭代,即为true,一旦开始迭代,调用next,置为false
//主要用来区分是否为第一次迭代
private boolean mBeforeStart = true;
IteratorWithAdditions() {
}
//如遇到发生删除时,只需要判断要删除的这个节点是否为mCurrent指向的节点
@SuppressWarnings("ReferenceEquality")
@Override
public void supportRemove(@NonNull Entry<K, V> entry) {
// 如果相等,意味着mCurrent指向的节点,已经被next返回返回出去了,但是这个节点在
//链表中即将要被删除,为了能保证正确无误的继续往下迭代,所以要把 mCurrent 这个指示器重新指向
//他的上一个,因为remove方法一旦执行完成,mCurrent执行的节点将从链表中干掉,那么mCurrent
//的上一个节点和mCurrent的下一个节点将跳过mCurrent直接互相指向并连接起来。所以只有把mCurrent
//这个指示器指向mCurrent的上一个这样才能正确的找到mCurrent的下一个
if (entry == mCurrent) {
mCurrent = mCurrent.mPrevious;
mBeforeStart = mCurrent == null;
}
}
@Override
public boolean hasNext() {
// 如果还没进行第一次迭代,
if (mBeforeStart) {
return mStart != null; // 判断头头结点是否为空个,不为空即表示有下一个
}
// 如果不是第一次迭代,那就判断上一次next 返回出去的entry是否还有下一个
return mCurrent != null && mCurrent.mNext != null;
}
@Override
public Map.Entry<K, V> next() {
if (mBeforeStart) { //如果是第一次迭代
mBeforeStart = false; // 状态置为false
mCurrent = mStart; // 返回头结点
} else {
// 不是第一次迭代,返回上一次next完后后,指示器指向的entry 的下一个
mCurrent = mCurrent != null ? mCurrent.mNext : null;
}
return mCurrent;
}
}
interface SupportRemove<K, V> {
void supportRemove(@NonNull Entry<K, V> entry);
}
// 用来包装 k 和 v
static class Entry<K, V> implements Map.Entry<K, V> {
@NonNull
final K mKey;
@NonNull
final V mValue;
Entry<K, V> mNext;
Entry<K, V> mPrevious;
Entry(@NonNull K key, @NonNull V value) {
mKey = key;
this.mValue = value;
}
@NonNull
@Override
public K getKey() {
return mKey;
}
@NonNull
@Override
public V getValue() {
return mValue;
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException("An entry modification is not supported");
}
@Override
public String toString() {
return mKey + "=" + mValue;
}
@SuppressWarnings("ReferenceEquality")
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Entry)) {
return false;
}
Entry entry = (Entry) obj;
return mKey.equals(entry.mKey) && mValue.equals(entry.mValue);
}
@Override
public int hashCode() {
return mKey.hashCode() ^ mValue.hashCode();
}
}
}