Remove shared MapCollections abstraction

This was only used to share an iterator implementation, but the required internal MapCollection subtype inside ArraySet was larger than just implementing the iterator directly. Removing it saves methods, fields, classes, bytecodes, and allocations at runtime.

The rest of MapCollections was inlined into ArrayMap, the sole location of use.

```
$ diffuse diff --jar before.jar after.jar
OLD: before.jar
NEW: after.jar

 JAR   │ old      │ new      │ diff
───────┼──────────┼──────────┼──────────
 class │ 84.9 KiB │   79 KiB │ -5.9 KiB
 other │     25 B │     25 B │      0 B
───────┼──────────┼──────────┼──────────
 total │   85 KiB │ 79.1 KiB │ -5.9 KiB

 CLASSES │ old │ new │ diff
─────────┼─────┼─────┼────────────────
 classes │  17 │  17 │   0 (+8 -8)
 methods │ 363 │ 336 │ -27 (+80 -107)
  fields │  77 │  74 │  -3 (+16 -19)
```

Test: gw :collection:collection:build
Change-Id: I41a7dc0bdf00cb3400c23dcfa0161b4024c4de3f
diff --git a/collection/collection/api/1.2.0-alpha01.ignore b/collection/collection/api/1.2.0-alpha01.ignore
new file mode 100644
index 0000000..7876d30
--- /dev/null
+++ b/collection/collection/api/1.2.0-alpha01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+InvalidNullConversion: androidx.collection.ArraySet#valueAt(int):
+    Attempted to remove @Nullable annotation from method androidx.collection.ArraySet.valueAt(int)
diff --git a/collection/collection/api/1.2.0-alpha01.txt b/collection/collection/api/1.2.0-alpha01.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/1.2.0-alpha01.txt
+++ b/collection/collection/api/1.2.0-alpha01.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/api/current.txt b/collection/collection/api/current.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/current.txt
+++ b/collection/collection/api/current.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/api/public_plus_experimental_1.2.0-alpha01.txt b/collection/collection/api/public_plus_experimental_1.2.0-alpha01.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/public_plus_experimental_1.2.0-alpha01.txt
+++ b/collection/collection/api/public_plus_experimental_1.2.0-alpha01.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/api/public_plus_experimental_current.txt b/collection/collection/api/public_plus_experimental_current.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/public_plus_experimental_current.txt
+++ b/collection/collection/api/public_plus_experimental_current.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/api/restricted_1.2.0-alpha01.ignore b/collection/collection/api/restricted_1.2.0-alpha01.ignore
new file mode 100644
index 0000000..7876d30
--- /dev/null
+++ b/collection/collection/api/restricted_1.2.0-alpha01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+InvalidNullConversion: androidx.collection.ArraySet#valueAt(int):
+    Attempted to remove @Nullable annotation from method androidx.collection.ArraySet.valueAt(int)
diff --git a/collection/collection/api/restricted_1.2.0-alpha01.txt b/collection/collection/api/restricted_1.2.0-alpha01.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/restricted_1.2.0-alpha01.txt
+++ b/collection/collection/api/restricted_1.2.0-alpha01.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/api/restricted_current.txt b/collection/collection/api/restricted_current.txt
index 7d0bd29..975bc87 100644
--- a/collection/collection/api/restricted_current.txt
+++ b/collection/collection/api/restricted_current.txt
@@ -37,7 +37,7 @@
     method public int size();
     method public Object![] toArray();
     method public <T> T![] toArray(T![]);
-    method public E? valueAt(int);
+    method public E! valueAt(int);
   }
 
   public final class CircularArray<E> {
diff --git a/collection/collection/src/main/java/androidx/collection/ArrayMap.java b/collection/collection/src/main/java/androidx/collection/ArrayMap.java
index b3b918c..51401cc 100644
--- a/collection/collection/src/main/java/androidx/collection/ArrayMap.java
+++ b/collection/collection/src/main/java/androidx/collection/ArrayMap.java
@@ -19,8 +19,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.lang.reflect.Array;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 /**
@@ -51,7 +54,9 @@
  * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
  */
 public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {
-    @Nullable MapCollections<K, V> mCollections;
+    @Nullable EntrySet mEntrySet;
+    @Nullable KeySet mKeySet;
+    @Nullable ValueCollection mValues;
 
     public ArrayMap() {
         super();
@@ -72,58 +77,6 @@
         super(map);
     }
 
-    private MapCollections<K, V> getCollection() {
-        if (mCollections == null) {
-            mCollections = new MapCollections<K, V>() {
-                @Override
-                protected int colGetSize() {
-                    return mSize;
-                }
-
-                @Override
-                protected Object colGetEntry(int index, int offset) {
-                    return mArray[(index<<1) + offset];
-                }
-
-                @Override
-                protected int colIndexOfKey(Object key) {
-                    return indexOfKey(key);
-                }
-
-                @Override
-                protected int colIndexOfValue(Object value) {
-                    return indexOfValue(value);
-                }
-
-                @Override
-                protected Map<K, V> colGetMap() {
-                    return ArrayMap.this;
-                }
-
-                @Override
-                protected void colPut(K key, V value) {
-                    put(key, value);
-                }
-
-                @Override
-                protected V colSetValue(int index, V value) {
-                    return setValueAt(index, value);
-                }
-
-                @Override
-                protected void colRemoveAt(int index) {
-                    removeAt(index);
-                }
-
-                @Override
-                protected void colClear() {
-                    clear();
-                }
-            };
-        }
-        return mCollections;
-    }
-
     /**
      * Determine if the array map contains all of the keys in the given collection.
      * @param collection The collection whose contents are to be checked against.
@@ -131,7 +84,12 @@
      * in <var>collection</var>, else returns false.
      */
     public boolean containsAll(@NonNull Collection<?> collection) {
-        return MapCollections.containsAllHelper(this, collection);
+        for (Object o : collection) {
+            if (!containsKey(o)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -152,7 +110,11 @@
      * @return Returns true if any keys were removed from the array map, else false.
      */
     public boolean removeAll(@NonNull Collection<?> collection) {
-        return MapCollections.removeAllHelper(this, collection);
+        int oldSize = mSize;
+        for (Object o : collection) {
+            remove(o);
+        }
+        return oldSize != mSize;
     }
 
     /**
@@ -162,7 +124,13 @@
      * @return Returns true if any keys were removed from the array map, else false.
      */
     public boolean retainAll(@NonNull Collection<?> collection) {
-        return MapCollections.retainAllHelper(this, collection);
+        int oldSize = mSize;
+        for (int i = mSize - 1; i >= 0; i--) {
+            if (!collection.contains(keyAt(i))) {
+                removeAt(i);
+            }
+        }
+        return oldSize != mSize;
     }
 
     /**
@@ -181,7 +149,11 @@
     @NonNull
     @Override
     public Set<Entry<K, V>> entrySet() {
-        return getCollection().getEntrySet();
+        Set<Entry<K, V>> entrySet = mEntrySet;
+        if (entrySet == null) {
+            entrySet = mEntrySet = new EntrySet();
+        }
+        return entrySet;
     }
 
     /**
@@ -194,7 +166,11 @@
     @NonNull
     @Override
     public Set<K> keySet() {
-        return getCollection().getKeySet();
+        Set<K> keySet = mKeySet;
+        if (keySet == null) {
+            keySet = mKeySet = new KeySet();
+        }
+        return keySet;
     }
 
     /**
@@ -207,6 +183,469 @@
     @NonNull
     @Override
     public Collection<V> values() {
-        return getCollection().getValues();
+        Collection<V> values = mValues;
+        if (values == null) {
+            values = mValues = new ValueCollection();
+        }
+        return values;
+    }
+
+    final class EntrySet implements Set<Map.Entry<K, V>> {
+        @Override
+        public boolean add(Map.Entry<K, V> object) {
+            // TODO support
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
+            int oldSize = mSize;
+            for (Map.Entry<K, V> entry : collection) {
+                put(entry.getKey(), entry.getValue());
+            }
+            return oldSize != mSize; // TODO broken heuristic
+        }
+
+        @Override
+        public void clear() {
+            ArrayMap.this.clear();
+        }
+
+        @Override
+        public boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
+            int index = indexOfKey(e.getKey());
+            if (index < 0) {
+                return false;
+            }
+            V foundVal = valueAt(index);
+            return ContainerHelpers.equal(foundVal, e.getValue());
+        }
+
+        @Override
+        public boolean containsAll(Collection<?> collection) {
+            for (Object o : collection) {
+                if (!contains(o)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return ArrayMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<Map.Entry<K, V>> iterator() {
+            return new MapIterator();
+        }
+
+        @Override
+        public boolean remove(Object object) {
+            // TODO support
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> collection) {
+            // TODO support
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> collection) {
+            // TODO support
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            return mSize;
+        }
+
+        @Override
+        public Object[] toArray() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public <T> T[] toArray(T[] array) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            return equalsSetHelper(this, object);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            for (int i=mSize-1; i>=0; i--) {
+                K key = keyAt(i);
+                V value = valueAt(i);
+                result += ( (key == null ? 0 : key.hashCode()) ^
+                        (value == null ? 0 : value.hashCode()) );
+            }
+            return result;
+        }
+    }
+
+    final class KeySet implements Set<K> {
+        @Override
+        public boolean add(K object) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(Collection<? extends K> collection) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            ArrayMap.this.clear();
+        }
+
+        @Override
+        public boolean contains(Object object) {
+            return containsKey(object);
+        }
+
+        @Override
+        public boolean containsAll(Collection<?> collection) {
+            return ArrayMap.this.containsAll(collection);
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return ArrayMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<K> iterator() {
+            return new KeyIterator();
+        }
+
+        @Override
+        public boolean remove(Object object) {
+            int index = indexOfKey(object);
+            if (index >= 0) {
+                removeAt(index);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> collection) {
+            return ArrayMap.this.removeAll(collection);
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> collection) {
+            return ArrayMap.this.retainAll(collection);
+        }
+
+        @Override
+        public int size() {
+            return mSize;
+        }
+
+        @Override
+        public Object[] toArray() {
+            final int N = mSize;
+            Object[] result = new Object[N];
+            for (int i=0; i<N; i++) {
+                result[i] = keyAt(i);
+            }
+            return result;
+        }
+
+        @Override
+        public <T> T[] toArray(T[] array) {
+            return toArrayHelper(array, 0);
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            return equalsSetHelper(this, object);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            for (int i=mSize-1; i>=0; i--) {
+                K obj = keyAt(i);
+                result += obj == null ? 0 : obj.hashCode();
+            }
+            return result;
+        }
+    }
+
+    final class ValueCollection implements Collection<V> {
+        @Override
+        public boolean add(V object) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(Collection<? extends V> collection) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            ArrayMap.this.clear();
+        }
+
+        @Override
+        public boolean contains(Object object) {
+            return indexOfValue(object) >= 0;
+        }
+
+        @Override
+        public boolean containsAll(Collection<?> collection) {
+            for (Object o : collection) {
+                if (!contains(o)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return ArrayMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<V> iterator() {
+            return new ValueIterator();
+        }
+
+        @Override
+        public boolean remove(Object object) {
+            int index = indexOfValue(object);
+            if (index >= 0) {
+                removeAt(index);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> collection) {
+            int N = mSize;
+            boolean changed = false;
+            for (int i=0; i<N; i++) {
+                V cur = valueAt(i);
+                if (collection.contains(cur)) {
+                    removeAt(i);
+                    i--;
+                    N--;
+                    changed = true;
+                }
+            }
+            return changed;
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> collection) {
+            int N = mSize;
+            boolean changed = false;
+            for (int i=0; i<N; i++) {
+                V cur = valueAt(i);
+                if (!collection.contains(cur)) {
+                    removeAt(i);
+                    i--;
+                    N--;
+                    changed = true;
+                }
+            }
+            return changed;
+        }
+
+        @Override
+        public int size() {
+            return mSize;
+        }
+
+        @Override
+        public Object[] toArray() {
+            final int N = mSize;
+            Object[] result = new Object[N];
+            for (int i=0; i<N; i++) {
+                result[i] = valueAt(i);
+            }
+            return result;
+        }
+
+        @Override
+        public <T> T[] toArray(T[] array) {
+            return toArrayHelper(array, 1);
+        }
+    }
+
+    final class KeyIterator extends IndexBasedArrayIterator<K> {
+        KeyIterator() {
+            super(ArrayMap.this.mSize);
+        }
+
+        @Override
+        protected K elementAt(int index) {
+            return keyAt(index);
+        }
+
+        @Override
+        protected void removeAt(int index) {
+            ArrayMap.this.removeAt(index);
+        }
+    }
+
+    final class ValueIterator extends IndexBasedArrayIterator<V> {
+        ValueIterator() {
+            super(ArrayMap.this.mSize);
+        }
+
+        @Override
+        protected V elementAt(int index) {
+            return valueAt(index);
+        }
+
+        @Override
+        protected void removeAt(int index) {
+            ArrayMap.this.removeAt(index);
+        }
+    }
+
+    final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> {
+        int mEnd;
+        int mIndex;
+        boolean mEntryValid;
+
+        MapIterator() {
+            mEnd = mSize - 1;
+            mIndex = -1;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return mIndex < mEnd;
+        }
+
+        @Override
+        public Map.Entry<K, V> next() {
+            if (!hasNext()) throw new NoSuchElementException();
+            mIndex++;
+            mEntryValid = true;
+            return this;
+        }
+
+        @Override
+        public void remove() {
+            if (!mEntryValid) {
+                throw new IllegalStateException();
+            }
+            removeAt(mIndex);
+            mIndex--;
+            mEnd--;
+            mEntryValid = false;
+        }
+
+        @Override
+        public K getKey() {
+            if (!mEntryValid) {
+                throw new IllegalStateException(
+                        "This container does not support retaining Map.Entry objects");
+            }
+            return keyAt(mIndex);
+        }
+
+        @Override
+        public V getValue() {
+            if (!mEntryValid) {
+                throw new IllegalStateException(
+                        "This container does not support retaining Map.Entry objects");
+            }
+            return valueAt(mIndex);
+        }
+
+        @Override
+        public V setValue(V object) {
+            if (!mEntryValid) {
+                throw new IllegalStateException(
+                        "This container does not support retaining Map.Entry objects");
+            }
+            return setValueAt(mIndex, object);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!mEntryValid) {
+                throw new IllegalStateException(
+                        "This container does not support retaining Map.Entry objects");
+            }
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
+            return ContainerHelpers.equal(e.getKey(), keyAt(mIndex))
+                    && ContainerHelpers.equal(e.getValue(), valueAt(mIndex));
+        }
+
+        @Override
+        public int hashCode() {
+            if (!mEntryValid) {
+                throw new IllegalStateException(
+                        "This container does not support retaining Map.Entry objects");
+            }
+            K key = keyAt(mIndex);
+            V value = valueAt(mIndex);
+            return (key == null ? 0 : key.hashCode()) ^
+                    (value == null ? 0 : value.hashCode());
+        }
+
+        @Override
+        public String toString() {
+            return getKey() + "=" + getValue();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    <T> T[] toArrayHelper(T[] array, int offset) {
+        final int N  = mSize;
+        if (array.length < N) {
+            @SuppressWarnings("unchecked") T[] newArray
+                    = (T[]) Array.newInstance(array.getClass().getComponentType(), N);
+            array = newArray;
+        }
+        for (int i=0; i<N; i++) {
+            array[i] = (T) mArray[(i<<1)+offset];
+        }
+        if (array.length > N) {
+            array[N] = null;
+        }
+        return array;
+    }
+
+    static <T> boolean equalsSetHelper(Set<T> set, Object object) {
+        if (set == object) {
+            return true;
+        }
+        if (object instanceof Set) {
+            Set<?> s = (Set<?>) object;
+
+            try {
+                return set.size() == s.size() && set.containsAll(s);
+            } catch (NullPointerException ignored) {
+            } catch (ClassCastException ignored) {
+            }
+        }
+        return false;
     }
 }
diff --git a/collection/collection/src/main/java/androidx/collection/ArraySet.java b/collection/collection/src/main/java/androidx/collection/ArraySet.java
index 541bd0a..a74e3f9 100644
--- a/collection/collection/src/main/java/androidx/collection/ArraySet.java
+++ b/collection/collection/src/main/java/androidx/collection/ArraySet.java
@@ -23,7 +23,6 @@
 import java.util.Collection;
 import java.util.ConcurrentModificationException;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -84,7 +83,6 @@
     Object[] mArray;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     int mSize;
-    private MapCollections<E, E> mCollections;
 
     private int binarySearch(int hash) {
         try {
@@ -381,7 +379,6 @@
      * @param index The desired index, must be between 0 and {@link #size()}-1.
      * @return Returns the value stored at the given index.
      */
-    @Nullable
     @SuppressWarnings("unchecked")
     public E valueAt(int index) {
         return (E) mArray[index];
@@ -690,74 +687,32 @@
         return buffer.toString();
     }
 
-    // ------------------------------------------------------------------------
-    // Interop with traditional Java containers.  Not as efficient as using
-    // specialized collection APIs.
-    // ------------------------------------------------------------------------
-
-    private MapCollections<E, E> getCollection() {
-        if (mCollections == null) {
-            mCollections = new MapCollections<E, E>() {
-                @Override
-                protected int colGetSize() {
-                    return mSize;
-                }
-
-                @Override
-                protected Object colGetEntry(int index, int offset) {
-                    return mArray[index];
-                }
-
-                @Override
-                protected int colIndexOfKey(Object key) {
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                protected int colIndexOfValue(Object value) {
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                protected Map<E, E> colGetMap() {
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                protected void colPut(E key, E value) {
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                protected E colSetValue(int index, E value) {
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                protected void colRemoveAt(int index) {
-                    removeAt(index);
-                }
-
-                @Override
-                protected void colClear() {
-                    throw new UnsupportedOperationException();
-                }
-            };
-        }
-        return mCollections;
-    }
-
     /**
      * Return an {@link java.util.Iterator} over all values in the set.
      *
-     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
-     * requires generating a number of temporary objects and allocates additional state
-     * information associated with the container that will remain for the life of the container.</p>
+     * <p><b>Note:</b> this is  aless efficient way to access the array contents compared to
+     * looping from 0 until {@link #size()} and calling {@link #valueAt(int)}.
      */
     @NonNull
     @Override
     public Iterator<E> iterator() {
-        return getCollection().new ArrayIterator<>(0);
+        return new ElementIterator();
+    }
+
+    private class ElementIterator extends IndexBasedArrayIterator<E> {
+        ElementIterator() {
+            super(mSize);
+        }
+
+        @Override
+        protected E elementAt(int index) {
+            return valueAt(index);
+        }
+
+        @Override
+        protected void removeAt(int index) {
+            ArraySet.this.removeAt(index);
+        }
     }
 
     /**
diff --git a/collection/collection/src/main/java/androidx/collection/IndexBasedArrayIterator.java b/collection/collection/src/main/java/androidx/collection/IndexBasedArrayIterator.java
new file mode 100644
index 0000000..9b10713
--- /dev/null
+++ b/collection/collection/src/main/java/androidx/collection/IndexBasedArrayIterator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.collection;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+abstract class IndexBasedArrayIterator<T> implements Iterator<T> {
+    private int mSize;
+    private int mIndex;
+    private boolean mCanRemove;
+
+    IndexBasedArrayIterator(int startingSize) {
+        mSize = startingSize;
+    }
+
+    protected abstract T elementAt(int index);
+    protected abstract void removeAt(int index);
+
+    @Override
+    public final boolean hasNext() {
+        return mIndex < mSize;
+    }
+
+    @Override
+    public T next() {
+        if (!hasNext()) throw new NoSuchElementException();
+        T res = elementAt(mIndex);
+        mIndex++;
+        mCanRemove = true;
+        return res;
+    }
+
+    @Override
+    public void remove() {
+        if (!mCanRemove) {
+            throw new IllegalStateException();
+        }
+        // Attempt removal first so an UnsupportedOperationException retains a valid state.
+        removeAt(mIndex--);
+        mSize--;
+        mCanRemove = false;
+    }
+}
diff --git a/collection/collection/src/main/java/androidx/collection/MapCollections.java b/collection/collection/src/main/java/androidx/collection/MapCollections.java
deleted file mode 100644
index ff74d7c..0000000
--- a/collection/collection/src/main/java/androidx/collection/MapCollections.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.collection;
-
-import androidx.annotation.Nullable;
-
-import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-/**
- * Helper for writing standard Java collection interfaces to a data
- * structure like {@link ArrayMap}.
- */
-abstract class MapCollections<K, V> {
-    @Nullable EntrySet mEntrySet;
-    @Nullable KeySet mKeySet;
-    @Nullable ValuesCollection mValues;
-
-    final class ArrayIterator<T> implements Iterator<T> {
-        final int mOffset;
-        int mSize;
-        int mIndex;
-        boolean mCanRemove = false;
-
-        ArrayIterator(int offset) {
-            mOffset = offset;
-            mSize = colGetSize();
-        }
-
-        @Override
-        public boolean hasNext() {
-            return mIndex < mSize;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public T next() {
-            if (!hasNext()) throw new NoSuchElementException();
-            Object res = colGetEntry(mIndex, mOffset);
-            mIndex++;
-            mCanRemove = true;
-            return (T)res;
-        }
-
-        @Override
-        public void remove() {
-            if (!mCanRemove) {
-                throw new IllegalStateException();
-            }
-            mIndex--;
-            mSize--;
-            mCanRemove = false;
-            colRemoveAt(mIndex);
-        }
-    }
-
-    final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> {
-        int mEnd;
-        int mIndex;
-        boolean mEntryValid = false;
-
-        MapIterator() {
-            mEnd = colGetSize() - 1;
-            mIndex = -1;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return mIndex < mEnd;
-        }
-
-        @Override
-        public Map.Entry<K, V> next() {
-            if (!hasNext()) throw new NoSuchElementException();
-            mIndex++;
-            mEntryValid = true;
-            return this;
-        }
-
-        @Override
-        public void remove() {
-            if (!mEntryValid) {
-                throw new IllegalStateException();
-            }
-            colRemoveAt(mIndex);
-            mIndex--;
-            mEnd--;
-            mEntryValid = false;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public K getKey() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return (K)colGetEntry(mIndex, 0);
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public V getValue() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return (V)colGetEntry(mIndex, 1);
-        }
-
-        @Override
-        public V setValue(V object) {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return colSetValue(mIndex, object);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            if (!(o instanceof Map.Entry)) {
-                return false;
-            }
-            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-            return ContainerHelpers.equal(e.getKey(), colGetEntry(mIndex, 0))
-                    && ContainerHelpers.equal(e.getValue(), colGetEntry(mIndex, 1));
-        }
-
-        @Override
-        public int hashCode() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            final Object key = colGetEntry(mIndex, 0);
-            final Object value = colGetEntry(mIndex, 1);
-            return (key == null ? 0 : key.hashCode()) ^
-                    (value == null ? 0 : value.hashCode());
-        }
-
-        @Override
-        public String toString() {
-            return getKey() + "=" + getValue();
-        }
-    }
-
-    final class EntrySet implements Set<Map.Entry<K, V>> {
-        @Override
-        public boolean add(Map.Entry<K, V> object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
-            int oldSize = colGetSize();
-            for (Map.Entry<K, V> entry : collection) {
-                colPut(entry.getKey(), entry.getValue());
-            }
-            return oldSize != colGetSize();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object o) {
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-            int index = colIndexOfKey(e.getKey());
-            if (index < 0) {
-                return false;
-            }
-            Object foundVal = colGetEntry(index, 1);
-            return ContainerHelpers.equal(foundVal, e.getValue());
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            Iterator<?> it = collection.iterator();
-            while (it.hasNext()) {
-                if (!contains(it.next())) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<Map.Entry<K, V>> iterator() {
-            return new MapIterator();
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            return equalsSetHelper(this, object);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 0;
-            for (int i=colGetSize()-1; i>=0; i--) {
-                final Object key = colGetEntry(i, 0);
-                final Object value = colGetEntry(i, 1);
-                result += ( (key == null ? 0 : key.hashCode()) ^
-                        (value == null ? 0 : value.hashCode()) );
-            }
-            return result;
-        }
-    };
-
-    final class KeySet implements Set<K> {
-
-        @Override
-        public boolean add(K object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends K> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object object) {
-            return colIndexOfKey(object) >= 0;
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            return containsAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<K> iterator() {
-            return new ArrayIterator<K>(0);
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            int index = colIndexOfKey(object);
-            if (index >= 0) {
-                colRemoveAt(index);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            return removeAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            return retainAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            return toArrayHelper(0);
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            return toArrayHelper(array, 0);
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            return equalsSetHelper(this, object);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 0;
-            for (int i=colGetSize()-1; i>=0; i--) {
-                Object obj = colGetEntry(i, 0);
-                result += obj == null ? 0 : obj.hashCode();
-            }
-            return result;
-        }
-    };
-
-    final class ValuesCollection implements Collection<V> {
-
-        @Override
-        public boolean add(V object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends V> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object object) {
-            return colIndexOfValue(object) >= 0;
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            Iterator<?> it = collection.iterator();
-            while (it.hasNext()) {
-                if (!contains(it.next())) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<V> iterator() {
-            return new ArrayIterator<V>(1);
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            int index = colIndexOfValue(object);
-            if (index >= 0) {
-                colRemoveAt(index);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            int N = colGetSize();
-            boolean changed = false;
-            for (int i=0; i<N; i++) {
-                Object cur = colGetEntry(i, 1);
-                if (collection.contains(cur)) {
-                    colRemoveAt(i);
-                    i--;
-                    N--;
-                    changed = true;
-                }
-            }
-            return changed;
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            int N = colGetSize();
-            boolean changed = false;
-            for (int i=0; i<N; i++) {
-                Object cur = colGetEntry(i, 1);
-                if (!collection.contains(cur)) {
-                    colRemoveAt(i);
-                    i--;
-                    N--;
-                    changed = true;
-                }
-            }
-            return changed;
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            return toArrayHelper(1);
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            return toArrayHelper(array, 1);
-        }
-    };
-
-    public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) {
-        Iterator<?> it = collection.iterator();
-        while (it.hasNext()) {
-            if (!map.containsKey(it.next())) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {
-        int oldSize = map.size();
-        Iterator<?> it = collection.iterator();
-        while (it.hasNext()) {
-            map.remove(it.next());
-        }
-        return oldSize != map.size();
-    }
-
-    public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) {
-        int oldSize = map.size();
-        Iterator<K> it = map.keySet().iterator();
-        while (it.hasNext()) {
-            if (!collection.contains(it.next())) {
-                it.remove();
-            }
-        }
-        return oldSize != map.size();
-    }
-
-
-    public Object[] toArrayHelper(int offset) {
-        final int N = colGetSize();
-        Object[] result = new Object[N];
-        for (int i=0; i<N; i++) {
-            result[i] = colGetEntry(i, offset);
-        }
-        return result;
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> T[] toArrayHelper(T[] array, int offset) {
-        final int N  = colGetSize();
-        if (array.length < N) {
-            @SuppressWarnings("unchecked") T[] newArray
-                = (T[]) Array.newInstance(array.getClass().getComponentType(), N);
-            array = newArray;
-        }
-        for (int i=0; i<N; i++) {
-            array[i] = (T)colGetEntry(i, offset);
-        }
-        if (array.length > N) {
-            array[N] = null;
-        }
-        return array;
-    }
-
-    public static <T> boolean equalsSetHelper(Set<T> set, Object object) {
-        if (set == object) {
-            return true;
-        }
-        if (object instanceof Set) {
-            Set<?> s = (Set<?>) object;
-
-            try {
-                return set.size() == s.size() && set.containsAll(s);
-            } catch (NullPointerException ignored) {
-                return false;
-            } catch (ClassCastException ignored) {
-                return false;
-            }
-        }
-        return false;
-    }
-
-    public Set<Map.Entry<K, V>> getEntrySet() {
-        if (mEntrySet == null) {
-            mEntrySet = new EntrySet();
-        }
-        return mEntrySet;
-    }
-
-    public Set<K> getKeySet() {
-        if (mKeySet == null) {
-            mKeySet = new KeySet();
-        }
-        return mKeySet;
-    }
-
-    public Collection<V> getValues() {
-        if (mValues == null) {
-            mValues = new ValuesCollection();
-        }
-        return mValues;
-    }
-
-    protected abstract int colGetSize();
-    protected abstract Object colGetEntry(int index, int offset);
-    protected abstract int colIndexOfKey(Object key);
-    protected abstract int colIndexOfValue(Object key);
-    protected abstract Map<K, V> colGetMap();
-    protected abstract void colPut(K key, V value);
-    protected abstract V colSetValue(int index, V value);
-    protected abstract void colRemoveAt(int index);
-    protected abstract void colClear();
-}
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index be1156a..c2cb71b 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -449,6 +449,10 @@
       "to": "ignore"
     },
     {
+      "from": "androidx/collection/IndexBasedArrayIterator(.*)",
+      "to": "android/support/v4/util/IndexBasedArrayIterator{0}"
+    },
+    {
       "from": "androidx/core/(.+)/(.+)Kt(.*)",
       "to": "ignore"
     },