Replace existing ArrayList implementation with faster, simpler one.
diff --git a/libcore/luni/src/main/java/java/util/ArrayList.java b/libcore/luni/src/main/java/java/util/ArrayList.java
index b8c7056..7c46e89 100644
--- a/libcore/luni/src/main/java/java/util/ArrayList.java
+++ b/libcore/luni/src/main/java/java/util/ArrayList.java
@@ -15,12 +15,16 @@
  *  limitations under the License.
  */
 
+// BEGIN android-note
+// New implementation: simpler and faster than Harmony implementation.
+// BEGIN android-note
+
 package java.util;
 
 import java.io.IOException;
+import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.io.ObjectStreamField;
 import java.io.Serializable;
 import java.lang.reflect.Array;
 
@@ -29,37 +33,38 @@
  * optional operations adding, removing, and replacing are supported. The
  * elements can be any objects.
  *
+ * @param <E> The element type of this list.
  * @since 1.2
  */
-public class ArrayList<E> extends AbstractList<E> implements List<E>,
-        Cloneable, Serializable, RandomAccess {
-
-    private static final long serialVersionUID = 8683452581122892189L;
-
-    // BEGIN android-added
-    /** zero-element array */
-    private static final Object[] emptyArray = new Object[0];
-    // END android-added
-
-    private transient int firstIndex;
-
-    private transient int lastIndex;
-
-    private transient E[] array;
+public class ArrayList<E> extends AbstractList<E>
+        implements Cloneable, Serializable, RandomAccess {
+    /**
+     * An empty array of objects (to be shared among all empty lists).
+     */
+    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 
     /**
-     * Constructs a new instance of {@code ArrayList} with zero capacity.
+     * The minimum amount by which the capacity of an ArrayList will increase.
+     * This tuning parameter controls a time-space tradeoff. This value (12)
+     * gives empirically good results and is arguably consistent with the
+     * RI's specified default initial capacity of 10: instead of 10, we start
+     * with 0 (sans allocation) and jump to 12.
      */
-    public ArrayList() {
-        // BEGIN android-changed
-        // default capacity is zero, not ten
-        this(0);
-        // END android-changed
-    }
+    private static final int MIN_CAPACITY_INCREMENT = 12;
+
+    /**
+     * The number of elements in this list.
+     */
+    int size;
+
+    /**
+     * The elements in this list, followed by nulls.
+     */
+    transient Object[] array;
 
     /**
      * Constructs a new instance of {@code ArrayList} with the specified
-     * capacity.
+     * initial capacity.
      *
      * @param capacity
      *            the initial capacity of this {@code ArrayList}.
@@ -68,84 +73,32 @@
         if (capacity < 0) {
             throw new IllegalArgumentException();
         }
-        firstIndex = lastIndex = 0;
-        array = newElementArray(capacity);
+        array = (capacity == 0 ? EMPTY_OBJECT_ARRAY : new Object[capacity]);
+    }
+
+    /**
+     * Constructs a new {@code ArrayList} instance with zero initial capacity.
+     */
+    public ArrayList() {
+        array = EMPTY_OBJECT_ARRAY;
     }
 
     /**
      * Constructs a new instance of {@code ArrayList} containing the elements of
-     * the specified collection. The initial size of the {@code ArrayList} will
-     * be 10% higher than the size of the specified collection.
+     * the specified collection.
      *
      * @param collection
      *            the collection of elements to add.
      */
     public ArrayList(Collection<? extends E> collection) {
-        firstIndex = 0;
-        Object[] objects = collection.toArray();
-        int size = objects.length;
-        array = newElementArray(size + (size / 10));
-        System.arraycopy(objects, 0, array, 0, size);
-        lastIndex = size;
-        modCount = 1;
-    }
-
-    @SuppressWarnings("unchecked")
-    private E[] newElementArray(int size) {
-        // BEGIN android-added
-        if (size == 0) {
-            return (E[])emptyArray;
+        Object[] a = collection.toArray();
+        if (a.getClass() != Object[].class) {
+            Object[] newArray = new Object[a.length];
+            System.arraycopy(a, 0, newArray, 0, a.length);
+            a = newArray;
         }
-        // END android-added
-
-        return (E[]) new Object[size];
-    }
-
-    /**
-     * Inserts the specified object into this {@code ArrayList} at the specified
-     * location. The object is inserted before any previous element at the
-     * specified location. If the location is equal to the size of this
-     * {@code ArrayList}, the object is added at the end.
-     *
-     * @param location
-     *            the index at which to insert the object.
-     * @param object
-     *            the object to add.
-     * @throws IndexOutOfBoundsException
-     *             when {@code location < 0 || > size()}
-     */
-    @Override
-    public void add(int location, E object) {
-        int size = lastIndex - firstIndex;
-        if (0 < location && location < size) {
-            if (firstIndex == 0 && lastIndex == array.length) {
-                growForInsert(location, 1);
-            } else if ((location < size / 2 && firstIndex > 0)
-                    || lastIndex == array.length) {
-                System.arraycopy(array, firstIndex, array, --firstIndex,
-                        location);
-            } else {
-                int index = location + firstIndex;
-                System.arraycopy(array, index, array, index + 1, size
-                        - location);
-                lastIndex++;
-            }
-            array[location + firstIndex] = object;
-        } else if (location == 0) {
-            if (firstIndex == 0) {
-                growAtFront(1);
-            }
-            array[--firstIndex] = object;
-        } else if (location == size) {
-            if (lastIndex == array.length) {
-                growAtEnd(1);
-            }
-            array[lastIndex++] = object;
-        } else {
-            throw new IndexOutOfBoundsException();
-        }
-
-        modCount++;
+        array = a;
+        size = a.length;
     }
 
     /**
@@ -155,80 +108,69 @@
      *            the object to add.
      * @return always true
      */
-    @Override
-    public boolean add(E object) {
-        if (lastIndex == array.length) {
-            growAtEnd(1);
+    @Override public boolean add(E object) {
+        Object[] a = array;
+        int s = size;
+        if (s == a.length) {
+            Object[] newArray = new Object[s +
+                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
+                     MIN_CAPACITY_INCREMENT : s >> 1)];
+            System.arraycopy(a, 0, newArray, 0, s);
+            array = a = newArray;
         }
-        array[lastIndex++] = object;
+        a[s] = object;
+        size = s + 1;
         modCount++;
         return true;
     }
 
     /**
-     * Inserts the objects in the specified collection at the specified location
-     * in this List. The objects are added in the order they are returned from
-     * the collection's iterator.
+     * Inserts the specified object into this {@code ArrayList} at the specified
+     * location. The object is inserted before any previous element at the
+     * specified location. If the location is equal to the size of this
+     * {@code ArrayList}, the object is added at the end.
      *
-     * @param location
-     *            the index at which to insert.
-     * @param collection
-     *            the collection of objects.
-     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
-     *         otherwise.
+     * @param index
+     *            the index at which to insert the object.
+     * @param object
+     *            the object to add.
      * @throws IndexOutOfBoundsException
      *             when {@code location < 0 || > size()}
      */
-    @Override
-    public boolean addAll(int location, Collection<? extends E> collection) {
-        int size = lastIndex - firstIndex;
-        if (location < 0 || location > size) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (this == collection) {
-            collection = (ArrayList)clone();
-        }
-        Object[] dumparray = collection.toArray();
-        int growSize = dumparray.length;
-        if (growSize == 0) {
-            return false;
+    @Override public void add(int index, E object) {
+        Object[] a = array;
+        int s = size;
+        if (index > s) {
+            throwIndexOutOfBoundsException(index, s);
         }
 
-        if (0 < location && location < size) {
-            if (array.length - size < growSize) {
-                growForInsert(location, growSize);
-            } else if ((location < size / 2 && firstIndex > 0)
-                    || lastIndex > array.length - growSize) {
-                int newFirst = firstIndex - growSize;
-                if (newFirst < 0) {
-                    int index = location + firstIndex;
-                    System.arraycopy(array, index, array, index - newFirst,
-                            size - location);
-                    lastIndex -= newFirst;
-                    newFirst = 0;
-                }
-                System.arraycopy(array, firstIndex, array, newFirst, location);
-                firstIndex = newFirst;
-            } else {
-                int index = location + firstIndex;
-                System.arraycopy(array, index, array, index + growSize, size
-                        - location);
-                lastIndex += growSize;
-            }
-        } else if (location == 0) {
-            growAtFront(growSize);
-            firstIndex -= growSize;
-        } else if (location == size) {
-            if (lastIndex > array.length - growSize) {
-                growAtEnd(growSize);
-            }
-            lastIndex += growSize;
+        if (s < a.length) {
+            System.arraycopy(a, index, a, index + 1, s - index);
+        } else {
+            // assert s == a.length;
+            Object[] newArray = new Object[newCapacity(s)];
+            System.arraycopy(a, 0, newArray, 0, index);
+            System.arraycopy(a, index, newArray, index + 1, s - index);
+            array = a = newArray;
         }
-
-        System.arraycopy(dumparray, 0, this.array, location + firstIndex,
-                growSize);
+        a[index] = object;
+        size = s + 1;
         modCount++;
-        return true;
+    }
+
+    /**
+     * This method controls the growth of ArrayList capacities.  It represents
+     * a time-space tradeoff: we don't want to grow lists too frequently
+     * (which wastes time and fragments storage), but we don't want to waste
+     * too much space in unused excess capacity.
+     *
+     * NOTE: This method is inlined into {@link #add(Object)} for performance.
+     * If you change the method, change it there too!
+     */
+    private static int newCapacity(int currentCapacity) {
+        int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
+                MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
+        return currentCapacity + increment;
     }
 
     /**
@@ -239,32 +181,85 @@
      * @return {@code true} if this {@code ArrayList} is modified, {@code false}
      *         otherwise.
      */
-    @Override
-    public boolean addAll(Collection<? extends E> collection) {
-        Object[] dumpArray = collection.toArray();
-        if (dumpArray.length == 0) {
+    @Override public boolean addAll(Collection<? extends E> collection) {
+        Object[] newPart = collection.toArray();
+        int newPartSize = newPart.length;
+        if (newPartSize == 0) {
             return false;
         }
-        if (dumpArray.length > array.length - lastIndex) {
-            growAtEnd(dumpArray.length);
+        Object[] a = array;
+        int s = size;
+        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
+        if (newSize > a.length) {
+            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
+            Object[] newArray = new Object[newCapacity];
+            System.arraycopy(a, 0, newArray, 0, s);
+            array = a = newArray;
         }
-        System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length);
-        lastIndex += dumpArray.length;
+        System.arraycopy(newPart, 0, a, s, newPartSize);
+        size = newSize;
         modCount++;
         return true;
     }
 
     /**
+     * Inserts the objects in the specified collection at the specified location
+     * in this List. The objects are added in the order they are returned from
+     * the collection's iterator.
+     *
+     * @param index
+     *            the index at which to insert.
+     * @param collection
+     *            the collection of objects.
+     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
+     *         otherwise.
+     * @throws IndexOutOfBoundsException
+     *             when {@code location < 0 || > size()}
+     */
+    @Override
+    public boolean addAll(int index, Collection<? extends E> collection) {
+        Object[] newPart = collection.toArray();
+        int newPartSize = newPart.length;
+        if (newPartSize == 0) {
+            return false;
+        }
+        Object[] a = array;
+        int s = size;
+        if (index > s) {
+            throwIndexOutOfBoundsException(index, s);
+        }
+        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
+        if (newSize <= a.length) {
+             System.arraycopy(a, index, a, index + newPartSize, s - index);
+        } else {
+            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
+            Object[] newArray = new Object[newCapacity];
+            System.arraycopy(a, 0, newArray, 0, index);
+            System.arraycopy(a, index, newArray, index + newPartSize, s-index);
+            array = a = newArray;
+        }
+        System.arraycopy(newPart, 0, a, index, newPartSize);
+        size = newSize;
+        modCount++;
+        return true;
+    }
+
+    /** This method was extracted to encourage VM to inline callers. */
+    private static void throwIndexOutOfBoundsException(int index, int size) {
+        throw new IndexOutOfBoundsException("Invalid index " + index
+                + ", size is " + size);
+    }
+
+    /**
      * Removes all elements from this {@code ArrayList}, leaving it empty.
      *
      * @see #isEmpty
      * @see #size
      */
-    @Override
-    public void clear() {
-        if (firstIndex != lastIndex) {
-            Arrays.fill(array, firstIndex, lastIndex, null);
-            firstIndex = lastIndex = 0;
+    @Override public void clear() {
+        if (size != 0) {
+            Arrays.fill(array, 0, size, null);
+            size = 0;
             modCount++;
         }
     }
@@ -276,45 +271,17 @@
      * @return a shallow copy of this {@code ArrayList}
      * @see java.lang.Cloneable
      */
-    @Override
-    @SuppressWarnings("unchecked")
-    public Object clone() {
+    @Override public Object clone() {
         try {
-            ArrayList<E> newList = (ArrayList<E>) super.clone();
-            newList.array = array.clone();
-            return newList;
+            ArrayList<?> result = (ArrayList<?>) super.clone();
+            result.array = array.clone();
+            return result;
         } catch (CloneNotSupportedException e) {
-            return null;
+           throw new AssertionError();
         }
     }
 
     /**
-     * Searches this {@code ArrayList} for the specified object.
-     *
-     * @param object
-     *            the object to search for.
-     * @return {@code true} if {@code object} is an element of this
-     *         {@code ArrayList}, {@code false} otherwise
-     */
-    @Override
-    public boolean contains(Object object) {
-        if (object != null) {
-            for (int i = firstIndex; i < lastIndex; i++) {
-                if (object.equals(array[i])) {
-                    return true;
-                }
-            }
-        } else {
-            for (int i = firstIndex; i < lastIndex; i++) {
-                if (array[i] == null) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Ensures that after this operation the {@code ArrayList} can hold the
      * specified number of elements without further growing.
      *
@@ -322,145 +289,93 @@
      *            the minimum capacity asked for.
      */
     public void ensureCapacity(int minimumCapacity) {
-        if (array.length < minimumCapacity) {
-            if (firstIndex > 0) {
-                growAtFront(minimumCapacity - array.length);
-            } else {
-                growAtEnd(minimumCapacity - array.length);
-            }
-        }
-    }
-
-    @Override
-    public E get(int location) {
-        // BEGIN android-changed: slight performance improvement
-        int _firstIndex = firstIndex;
-        if (0 <= location && location < lastIndex - _firstIndex) {
-            return array[_firstIndex + location];
-        }
-        throw new IndexOutOfBoundsException("Invalid location " + location
-                + ", size is " + (lastIndex - _firstIndex));
-        // END android-changed
-    }
-
-    private void growAtEnd(int required) {
-        int size = lastIndex - firstIndex;
-        if (firstIndex >= required - (array.length - lastIndex)) {
-            int newLast = lastIndex - firstIndex;
-            if (size > 0) {
-                System.arraycopy(array, firstIndex, array, 0, size);
-                int start = newLast < firstIndex ? firstIndex : newLast;
-                Arrays.fill(array, start, array.length, null);
-            }
-            firstIndex = 0;
-            lastIndex = newLast;
-        } else {
-            int increment = size / 2;
-            if (required > increment) {
-                increment = required;
-            }
-            if (increment < 12) {
-                increment = 12;
-            }
-            E[] newArray = newElementArray(size + increment);
-            if (size > 0) {
-                System.arraycopy(array, firstIndex, newArray, 0, size);
-                firstIndex = 0;
-                lastIndex = size;
-            }
+        Object[] a = array;
+        if (a.length < minimumCapacity) {
+            Object[] newArray = new Object[minimumCapacity];
+            System.arraycopy(a, 0, newArray, 0, size);
             array = newArray;
+            modCount++;
         }
     }
 
-    private void growAtFront(int required) {
-        int size = lastIndex - firstIndex;
-        if (array.length - lastIndex + firstIndex >= required) {
-            int newFirst = array.length - size;
-            if (size > 0) {
-                System.arraycopy(array, firstIndex, array, newFirst, size);
-                int length = firstIndex + size > newFirst ? newFirst
-                        : firstIndex + size;
-                Arrays.fill(array, firstIndex, length, null);
-            }
-            firstIndex = newFirst;
-            lastIndex = array.length;
-        } else {
-            int increment = size / 2;
-            if (required > increment) {
-                increment = required;
-            }
-            if (increment < 12) {
-                increment = 12;
-            }
-            E[] newArray = newElementArray(size + increment);
-            if (size > 0) {
-                System.arraycopy(array, firstIndex, newArray, newArray.length
-                        - size, size);
-            }
-            firstIndex = newArray.length - size;
-            lastIndex = newArray.length;
-            array = newArray;
+    @SuppressWarnings("unchecked") @Override public E get(int index) {
+        if (index >= size) {
+            throwIndexOutOfBoundsException(index, size);
         }
+        return (E) array[index];
     }
 
-    private void growForInsert(int location, int required) {
-        int size = lastIndex - firstIndex;
-        int increment = size / 2;
-        if (required > increment) {
-            increment = required;
-        }
-        if (increment < 12) {
-            increment = 12;
-        }
-        E[] newArray = newElementArray(size + increment);
-        int newFirst = increment - required;
-        // Copy elements after location to the new array skipping inserted
-        // elements
-        System.arraycopy(array, location + firstIndex, newArray, newFirst
-                + location + required, size - location);
-        // Copy elements before location to the new array from firstIndex
-        System.arraycopy(array, firstIndex, newArray, newFirst, location);
-        firstIndex = newFirst;
-        lastIndex = size + increment;
-
-        array = newArray;
+    /**
+     * Returns the number of elements in this {@code ArrayList}.
+     *
+     * @return the number of elements in this {@code ArrayList}.
+     */
+    @Override public int size() {
+        return size;
     }
 
-    @Override
-    public int indexOf(Object object) {
+    @Override public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Searches this {@code ArrayList} for the specified object.
+     *
+     * @param object
+     *            the object to search for.
+     * @return {@code true} if {@code object} is an element of this
+     *         {@code ArrayList}, {@code false} otherwise
+     */
+    @Override public boolean contains(Object object) {
+        Object[] a = array;
+        int s = size;
         if (object != null) {
-            for (int i = firstIndex; i < lastIndex; i++) {
-                if (object.equals(array[i])) {
-                    return i - firstIndex;
+            for (int i = 0; i < s; i++) {
+                if (object.equals(a[i])) {
+                    return true;
                 }
             }
         } else {
-            for (int i = firstIndex; i < lastIndex; i++) {
-                if (array[i] == null) {
-                    return i - firstIndex;
+            for (int i = 0; i < s; i++) {
+                if (a[i] == null) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override public int indexOf(Object object) {
+        Object[] a = array;
+        int s = size;
+        if (object != null) {
+            for (int i = 0; i < s; i++) {
+                if (object.equals(a[i])) {
+                    return i;
+                }
+            }
+        } else {
+            for (int i = 0; i < s; i++) {
+                if (a[i] == null) {
+                    return i;
                 }
             }
         }
         return -1;
     }
 
-    @Override
-    public boolean isEmpty() {
-        return lastIndex == firstIndex;
-    }
-
-    @Override
-    public int lastIndexOf(Object object) {
+    @Override public int lastIndexOf(Object object) {
+        Object[] a = array;
         if (object != null) {
-            for (int i = lastIndex - 1; i >= firstIndex; i--) {
-                if (object.equals(array[i])) {
-                    return i - firstIndex;
+            for (int i = size - 1; i >= 0; i--) {
+                if (object.equals(a[i])) {
+                    return i;
                 }
             }
         } else {
-            for (int i = lastIndex - 1; i >= firstIndex; i--) {
-                if (array[i] == null) {
-                    return i - firstIndex;
+            for (int i = size - 1; i >= 0; i--) {
+                if (a[i] == null) {
+                    return i;
                 }
             }
         }
@@ -470,99 +385,81 @@
     /**
      * Removes the object at the specified location from this list.
      *
-     * @param location
+     * @param index
      *            the index of the object to remove.
      * @return the removed object.
      * @throws IndexOutOfBoundsException
      *             when {@code location < 0 || >= size()}
      */
-    @Override
-    public E remove(int location) {
-        E result;
-        int size = lastIndex - firstIndex;
-        if (0 <= location && location < size) {
-            if (location == size - 1) {
-                result = array[--lastIndex];
-                array[lastIndex] = null;
-            } else if (location == 0) {
-                result = array[firstIndex];
-                array[firstIndex++] = null;
-            } else {
-                int elementIndex = firstIndex + location;
-                result = array[elementIndex];
-                if (location < size / 2) {
-                    System.arraycopy(array, firstIndex, array, firstIndex + 1,
-                            location);
-                    array[firstIndex++] = null;
-                } else {
-                    System.arraycopy(array, elementIndex + 1, array,
-                            elementIndex, size - location - 1);
-                    array[--lastIndex] = null;
-                }
-            }
-            if (firstIndex == lastIndex) {
-                firstIndex = lastIndex = 0;
-            }
-        } else {
-            throw new IndexOutOfBoundsException();
+    @Override public E remove(int index) {
+        Object[] a = array;
+        int s = size;
+        if (index >= s) {
+            throwIndexOutOfBoundsException(index, s);
         }
-
+        @SuppressWarnings("unchecked") E result = (E) a[index];
+        System.arraycopy(a, index + 1, a, index, --s - index);
+        a[s] = null;  // Prevent memory leak
+        size = s;
         modCount++;
         return result;
     }
 
-    @Override
-    public boolean remove(Object object) {
-        int location = indexOf(object);
-        if (location >= 0) {
-            remove(location);
-            return true;
+    @Override public boolean remove(Object object) {
+        Object[] a = array;
+        int s = size;
+        if (object != null) {
+            for (int i = 0; i < s; i++) {
+                if (object.equals(a[i])) {
+                    System.arraycopy(a, i + 1, a, i, --s - i);
+                    a[s] = null;  // Prevent memory leak
+                    size = s;
+                    modCount++;
+                    return true;
+                }
+            }
+        } else {
+            for (int i = 0; i < s; i++) {
+                if (a[i] == null) {
+                    System.arraycopy(a, i + 1, a, i, --s - i);
+                    a[s] = null;  // Prevent memory leak
+                    size = s;
+                    modCount++;
+                    return true;
+                }
+            }
         }
         return false;
     }
 
-    /**
-     * Removes the objects in the specified range from the start to the end, but
-     * not including the end index.
-     *
-     * @param start
-     *            the index at which to start removing.
-     * @param end
-     *            the index one after the end of the range to remove.
-     * @throws IndexOutOfBoundsException
-     *             when {@code start < 0, start > end} or {@code end > size()}
-     */
-    @Override
-    protected void removeRange(int start, int end) {
-        if (start >= 0 && start <= end && end <= (lastIndex - firstIndex)) {
-            if (start == end) {
-                return;
-            }
-            int size = lastIndex - firstIndex;
-            if (end == size) {
-                Arrays.fill(array, firstIndex + start, lastIndex, null);
-                lastIndex = firstIndex + start;
-            } else if (start == 0) {
-                Arrays.fill(array, firstIndex, firstIndex + end, null);
-                firstIndex += end;
-            } else {
-                System.arraycopy(array, firstIndex + end, array, firstIndex
-                        + start, size - end);
-                int newLast = lastIndex + start - end;
-                Arrays.fill(array, newLast, lastIndex, null);
-                lastIndex = newLast;
-            }
-            modCount++;
-        } else {
-            throw new IndexOutOfBoundsException();
+    @Override protected void removeRange(int fromIndex, int toIndex) {
+        Object[] a = array;
+        int s = size;
+        if (fromIndex >= s) {
+            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
+                    + " >= size " + size);
         }
+        if (toIndex > s) {
+            throw new IndexOutOfBoundsException("toIndex " + toIndex
+                    + " > size " + size);
+        }
+        if (fromIndex > toIndex) {
+            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
+                    + " > toIndex " + toIndex);
+        }
+
+        System.arraycopy(a, toIndex, a, fromIndex, s - toIndex);
+        int rangeSize = toIndex - fromIndex;
+        Arrays.fill(a, s - rangeSize, s, null);
+        size = s - rangeSize;
+        modCount++;
     }
 
     /**
      * Replaces the element at the specified location in this {@code ArrayList}
      * with the specified object.
      *
-     * @param location
+     * @param index
      *            the index at which to put the specified object.
      * @param object
      *            the object to add.
@@ -570,24 +467,14 @@
      * @throws IndexOutOfBoundsException
      *             when {@code location < 0 || >= size()}
      */
-    @Override
-    public E set(int location, E object) {
-        if (0 <= location && location < (lastIndex - firstIndex)) {
-            E result = array[firstIndex + location];
-            array[firstIndex + location] = object;
-            return result;
+    @Override public E set(int index, E object) {
+        Object[] a = array;
+        if (index >= size) {
+            throwIndexOutOfBoundsException(index, size);
         }
-        throw new IndexOutOfBoundsException();
-    }
-
-    /**
-     * Returns the number of elements in this {@code ArrayList}.
-     *
-     * @return the number of elements in this {@code ArrayList}.
-     */
-    @Override
-    public int size() {
-        return lastIndex - firstIndex;
+        @SuppressWarnings("unchecked") E result = (E) a[index];
+        a[index] = object;
+        return result;
     }
 
     /**
@@ -596,11 +483,10 @@
      *
      * @return an array of the elements from this {@code ArrayList}
      */
-    @Override
-    public Object[] toArray() {
-        int size = lastIndex - firstIndex;
-        Object[] result = new Object[size];
-        System.arraycopy(array, firstIndex, result, 0, size);
+    @Override public Object[] toArray() {
+        int s = size;
+        Object[] result = new Object[s];
+        System.arraycopy(array, 0, result, 0, s);
         return result;
     }
 
@@ -619,17 +505,16 @@
      *             when the type of an element in this {@code ArrayList} cannot
      *             be stored in the type of the specified array.
      */
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> T[] toArray(T[] contents) {
-        int size = lastIndex - firstIndex;
-        if (size > contents.length) {
-            Class<?> ct = contents.getClass().getComponentType();
-            contents = (T[]) Array.newInstance(ct, size);
+    @Override public <T> T[] toArray(T[] contents) {
+        int s = size;
+        if (contents.length < s) {
+            @SuppressWarnings("unchecked") T[] newArray
+                = (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
+            contents = newArray;
         }
-        System.arraycopy(array, firstIndex, contents, 0, size);
-        if (size < contents.length) {
-            contents[size] = null;
+        System.arraycopy(this.array, 0, contents, 0, s);
+        if (contents.length > s) {
+            contents[s] = null;
         }
         return contents;
     }
@@ -641,37 +526,132 @@
      * @see #size
      */
     public void trimToSize() {
-        int size = lastIndex - firstIndex;
-        E[] newArray = newElementArray(size);
-        System.arraycopy(array, firstIndex, newArray, 0, size);
-        array = newArray;
-        firstIndex = 0;
-        lastIndex = array.length;
-        modCount = 0;
+        int s = size;
+        if (s == array.length) {
+            return;
+        }
+        if (s == 0) {
+            array = EMPTY_OBJECT_ARRAY;
+        } else {
+            Object[] newArray = new Object[s];
+            System.arraycopy(array, 0, newArray, 0, s);
+            array = newArray;
+        }
+        modCount++;
     }
 
-    private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField(
-            "size", Integer.TYPE) }; //$NON-NLS-1$
+    @Override public Iterator<E> iterator() {
+        return new ArrayListIterator();
+    }
+
+    private class ArrayListIterator implements Iterator<E> {
+        /** Number of elements remaining in this iteration */
+        private int remaining = size;
+
+        /** Index of element that remove() would remove, or -1 if no such elt */
+        private int removalIndex = -1;
+
+        /** The expected modCount value */
+        private int expectedModCount = modCount;
+
+        public boolean hasNext() {
+            return remaining != 0;
+        }
+
+        @SuppressWarnings("unchecked") public E next() {
+            ArrayList<E> ourList = ArrayList.this;
+            int rem = remaining;
+            if (ourList.modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+            if (rem == 0) {
+                throw new NoSuchElementException();
+            }
+            remaining = rem - 1;
+            return (E) ourList.array[removalIndex = ourList.size - rem];
+        }
+
+        public void remove() {
+            Object[] a = array;
+            int removalIdx = removalIndex;
+            if (modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+            if (removalIdx < 0) {
+                throw new IllegalStateException();
+            }
+            System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
+            a[--size] = null;  // Prevent memory leak
+            removalIndex = -1;
+            expectedModCount = ++modCount;
+        }
+    }
+
+    @Override public int hashCode() {
+        Object[] a = array;
+        int hashCode = 1;
+        for (int i = 0, s = size; i < s; i++) {
+            Object e = a[i];
+            hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
+        }
+        return hashCode;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof List)) {
+            return false;
+        }
+        List<?> that = (List<?>) o;
+        int s = size;
+        if (that.size() != s) {
+            return false;
+        }
+        Object[] a = array;
+        if (that instanceof RandomAccess) {
+            for (int i = 0; i < s; i++) {
+                Object eThis = a[i];
+                Object ethat = that.get(i);
+                if (eThis == null ? ethat != null : !eThis.equals(ethat)) {
+                    return false;
+                }
+            }
+        } else {  // Argument list is not random access; use its iterator
+            Iterator<?> it = that.iterator();
+            for (int i = 0; i < s; i++) {
+                Object eThis = a[i];
+                Object eThat = it.next();
+                if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private static final long serialVersionUID = 8683452581122892189L;
 
     private void writeObject(ObjectOutputStream stream) throws IOException {
-        ObjectOutputStream.PutField fields = stream.putFields();
-        fields.put("size", lastIndex - firstIndex); //$NON-NLS-1$
-        stream.writeFields();
+        stream.defaultWriteObject();
         stream.writeInt(array.length);
-        Iterator<?> it = iterator();
-        while (it.hasNext()) {
-            stream.writeObject(it.next());
+        for (int i = 0; i < size; i++) {
+            stream.writeObject(array[i]);
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void readObject(ObjectInputStream stream) throws IOException,
             ClassNotFoundException {
-        ObjectInputStream.GetField fields = stream.readFields();
-        lastIndex = fields.get("size", 0); //$NON-NLS-1$
-        array = newElementArray(stream.readInt());
-        for (int i = 0; i < lastIndex; i++) {
-            array[i] = (E) stream.readObject();
+        stream.defaultReadObject();
+        int cap = stream.readInt();
+        if (cap < size) {
+            throw new InvalidObjectException(
+                    "Capacity: " + cap + " < size: " + size);
+        }
+        array = (cap == 0 ? EMPTY_OBJECT_ARRAY : new Object[cap]);
+        for (int i = 0; i < size; i++) {
+            array[i] = stream.readObject();
         }
     }
-}
+ }
diff --git a/libcore/luni/src/test/java/tests/api/java/util/ArrayListTest.java b/libcore/luni/src/test/java/tests/api/java/util/ArrayListTest.java
index 8aa77cc..0356731 100644
--- a/libcore/luni/src/test/java/tests/api/java/util/ArrayListTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/util/ArrayListTest.java
@@ -19,7 +19,7 @@
 import dalvik.annotation.TestTargetNew;
 import dalvik.annotation.TestTargets;
 import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass; 
+import dalvik.annotation.TestTargetClass;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -33,13 +33,13 @@
 
 import tests.support.Support_ListTest;
 
-@TestTargetClass(ArrayList.class) 
+@TestTargetClass(ArrayList.class)
 public class ArrayListTest extends junit.framework.TestCase {
 
     List alist;
 
     Object[] objArray;
-    
+
     /**
      * @tests java.util.ArrayList#ArrayList()
      */
@@ -72,7 +72,7 @@
         // Test for method java.util.ArrayList(int)
         ArrayList al = new ArrayList(5);
         assertEquals("Incorrect arrayList created", 0, al.size());
-        
+
         try {
             new ArrayList(-10);
             fail("IllegalArgumentException expected");
@@ -130,14 +130,14 @@
         assertNull("Should have returned null", alist.get(25));
         assertTrue("Should have returned the old item from slot 25", alist
                 .get(26) == oldItem);
-        
+
         try {
             alist.add(-1, null);
             fail("IndexOutOfBoundsException expected");
         } catch (IndexOutOfBoundsException e) {
             //expected
         }
-        
+
         try {
             alist.add(alist.size() + 1, null);
             fail("IndexOutOfBoundsException expected");
@@ -198,9 +198,9 @@
         assertTrue("Incorrect size: " + alist.size(), alist.size() == 205);
         assertNull("Item at slot 100 should be null", alist.get(100));
         assertNull("Item at slot 101 should be null", alist.get(101));
-        assertEquals("Item at slot 102 should be 'yoink'", 
+        assertEquals("Item at slot 102 should be 'yoink'",
                 "yoink", alist.get(102));
-        assertEquals("Item at slot 103 should be 'kazoo'", 
+        assertEquals("Item at slot 103 should be 'kazoo'",
                 "kazoo", alist.get(103));
         assertNull("Item at slot 104 should be null", alist.get(104));
         alist.addAll(205, listWithNulls);
@@ -228,24 +228,29 @@
         }
     }
 
-    /**
-     * @tests java.util.ArrayList#addAll(int, java.util.Collection)
-     */
-    @TestTargetNew(
-        level = TestLevel.PARTIAL_COMPLETE,
-        notes = "Verifies IndexOutOfBoundsException.",
-        method = "addAll",
-        args = {int.class, java.util.Collection.class}
-    )
-    public void test_addAllILjava_util_Collection_2() {
-        // Regression for HARMONY-467
-        ArrayList obj = new ArrayList();
-        try {
-            obj.addAll((int) -1, (Collection) null);
-            fail("IndexOutOfBoundsException expected");
-        } catch (IndexOutOfBoundsException e) {
-        }
-    }
+// BEGIN android-removed
+// The spec does not mandate that IndexOutOfBoundsException be thrown in
+// preference to NullPointerException when the caller desserves both.
+//
+//    /**
+//     * @tests java.util.ArrayList#addAll(int, java.util.Collection)
+//     */
+//    @TestTargetNew(
+//        level = TestLevel.PARTIAL_COMPLETE,
+//        notes = "Verifies IndexOutOfBoundsException.",
+//        method = "addAll",
+//        args = {int.class, java.util.Collection.class}
+//    )
+//    public void test_addAllILjava_util_Collection_2() {
+//        // Regression for HARMONY-467
+//        ArrayList obj = new ArrayList();
+//        try {
+//            obj.addAll((int) -1, (Collection) null);
+//            fail("IndexOutOfBoundsException expected");
+//        } catch (IndexOutOfBoundsException e) {
+//        }
+//    }
+// END android-removed
 
     /**
      * @tests java.util.ArrayList#addAll(java.util.Collection)
@@ -287,17 +292,17 @@
                 .get(101) == i.next());
         assertTrue("Item at slot 103 is wrong: " + alist.get(102), alist
                 .get(102) == i.next());
-        
-        
+
+
         // Regression test for Harmony-3481
         ArrayList<Integer> originalList = new ArrayList<Integer>(12);
         for (int j = 0; j < 12; j++) {
             originalList.add(j);
         }
-        
+
         originalList.remove(0);
         originalList.remove(0);
-        
+
         ArrayList<Integer> additionalList = new ArrayList<Integer>(11);
         for (int j = 0; j < 11; j++) {
             additionalList.add(j);
@@ -672,7 +677,7 @@
                 assertTrue("Returned incorrect array: " + i,
                         retArray[i] == objArray[i]);
         }
-        
+
         String[] strArray = new String[100];
         try {
             alist.toArray(strArray);
@@ -746,16 +751,16 @@
 
         list.remove(0);
         assertEquals(1, list.size());
-        
+
         ArrayList collection = new ArrayList();
         collection.add("1");
         collection.add("2");
         collection.add("3");
         assertEquals(3, collection.size());
-        
+
         list.addAll(0, collection);
         assertEquals(4, list.size());
-        
+
         list.remove(0);
         list.remove(0);
         assertEquals(2, list.size());
@@ -769,13 +774,13 @@
         collection.add("10");
         collection.add("11");
         collection.add("12");
-        
+
         assertEquals(12, collection.size());
-        
+
         list.addAll(0, collection);
         assertEquals(14, list.size());
     }
-    
+
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         notes = "",
@@ -818,7 +823,7 @@
         mal.add("f");
         mal.add("g");
         mal.add("h");
-        
+
         mal.removeRange(2, 4);
 
         String[] result = new String[6];
@@ -827,30 +832,30 @@
                 new String[] { "a", "b", "e", "f", "g", "h"}));
     }
 
-    
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
      */
     protected void setUp() throws Exception {
         super.setUp();
-        
+
         objArray = new Object[100];
         for (int i = 0; i < objArray.length; i++) {
             objArray[i] = new Integer(i);
         }
-        
+
         alist = new ArrayList();
         for (int i = 0; i < objArray.length; i++) {
             alist.add(objArray[i]);
         }
     }
-    
+
     @Override
     protected void tearDown() throws Exception {
         objArray = null;
         alist = null;
-        
+
         super.tearDown();
     }
 }