--- jsr166/src/main/java/util/ArrayList.java 2011/05/07 12:22:02 1.32 +++ jsr166/src/main/java/util/ArrayList.java 2016/10/17 21:46:27 1.33 @@ -1,12 +1,12 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this + * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -25,34 +25,38 @@ package java.util; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + /** - * Resizable-array implementation of the List interface. Implements + * Resizable-array implementation of the {@code List} interface. Implements * all optional list operations, and permits all elements, including - * null. In addition to implementing the List interface, + * {@code null}. In addition to implementing the {@code List} interface, * this class provides methods to manipulate the size of the array that is * used internally to store the list. (This class is roughly equivalent to - * Vector, except that it is unsynchronized.) + * {@code Vector}, except that it is unsynchronized.) * - *

The size, isEmpty, get, set, - * iterator, and listIterator operations run in constant - * time. The add operation runs in amortized constant time, + *

The {@code size}, {@code isEmpty}, {@code get}, {@code set}, + * {@code iterator}, and {@code listIterator} operations run in constant + * time. The {@code add} operation runs in amortized constant time, * that is, adding n elements requires O(n) time. All of the other operations * run in linear time (roughly speaking). The constant factor is low compared - * to that for the LinkedList implementation. + * to that for the {@code LinkedList} implementation. * - *

Each ArrayList instance has a capacity. The capacity is + *

Each {@code ArrayList} instance has a capacity. The capacity is * the size of the array used to store the elements in the list. It is always * at least as large as the list size. As elements are added to an ArrayList, * its capacity grows automatically. The details of the growth policy are not * specified beyond the fact that adding an element has constant amortized * time cost. * - *

An application can increase the capacity of an ArrayList instance - * before adding a large number of elements using the ensureCapacity + *

An application can increase the capacity of an {@code ArrayList} instance + * before adding a large number of elements using the {@code ensureCapacity} * operation. This may reduce the amount of incremental reallocation. * *

Note that this implementation is not synchronized. - * If multiple threads access an ArrayList instance concurrently, + * If multiple threads access an {@code ArrayList} instance concurrently, * and at least one of the threads modifies the list structurally, it * must be synchronized externally. (A structural modification is * any operation that adds or deletes one or more elements, or explicitly @@ -66,7 +70,7 @@ package java.util; * unsynchronized access to the list:

  *   List list = Collections.synchronizedList(new ArrayList(...));
* - *

+ *

* The iterators returned by this class's {@link #iterator() iterator} and * {@link #listIterator(int) listIterator} methods are fail-fast: * if the list is structurally modified at any time after the iterator is @@ -90,6 +94,8 @@ package java.util; * * Java Collections Framework. * + * @param the type of elements in this list + * * @author Josh Bloch * @author Neal Gafter * @see Collection @@ -105,10 +111,29 @@ public class ArrayList extends Abstra private static final long serialVersionUID = 8683452581122892189L; /** + * Default initial capacity. + */ + private static final int DEFAULT_CAPACITY = 10; + + /** + * Shared empty array instance used for empty instances. + */ + private static final Object[] EMPTY_ELEMENTDATA = {}; + + /** + * Shared empty array instance used for default sized empty instances. We + * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when + * first element is added. + */ + private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; + + /** * The array buffer into which the elements of the ArrayList are stored. - * The capacity of the ArrayList is the length of this array buffer. + * The capacity of the ArrayList is the length of this array buffer. Any + * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA + * will be expanded to DEFAULT_CAPACITY when the first element is added. */ - private transient Object[] elementData; + transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). @@ -125,18 +150,21 @@ public class ArrayList extends Abstra * is negative */ public ArrayList(int initialCapacity) { - super(); - if (initialCapacity < 0) + if (initialCapacity > 0) { + this.elementData = new Object[initialCapacity]; + } else if (initialCapacity == 0) { + this.elementData = EMPTY_ELEMENTDATA; + } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); - this.elementData = new Object[initialCapacity]; + } } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { - this(10); + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** @@ -149,45 +177,105 @@ public class ArrayList extends Abstra */ public ArrayList(Collection c) { elementData = c.toArray(); - size = elementData.length; - // c.toArray might (incorrectly) not return Object[] (see 6260652) - if (elementData.getClass() != Object[].class) - elementData = Arrays.copyOf(elementData, size, Object[].class); + if ((size = elementData.length) != 0) { + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) + if (elementData.getClass() != Object[].class) + elementData = Arrays.copyOf(elementData, size, Object[].class); + } else { + // replace with empty array. + this.elementData = EMPTY_ELEMENTDATA; + } } /** - * Trims the capacity of this ArrayList instance to be the + * Trims the capacity of this {@code ArrayList} instance to be the * list's current size. An application can use this operation to minimize - * the storage of an ArrayList instance. + * the storage of an {@code ArrayList} instance. */ public void trimToSize() { modCount++; - int oldCapacity = elementData.length; - if (size < oldCapacity) { - elementData = Arrays.copyOf(elementData, size); + if (size < elementData.length) { + elementData = (size == 0) + ? EMPTY_ELEMENTDATA + : Arrays.copyOf(elementData, size); } } /** - * Increases the capacity of this ArrayList instance, if + * Increases the capacity of this {@code ArrayList} instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * - * @param minCapacity the desired minimum capacity + * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { - modCount++; - int oldCapacity = elementData.length; - if (minCapacity > oldCapacity) { - int newCapacity = (oldCapacity * 3)/2 + 1; - if (newCapacity < minCapacity) - newCapacity = minCapacity; - // minCapacity is usually close to size, so this is a win: - elementData = Arrays.copyOf(elementData, newCapacity); + if (minCapacity > elementData.length + && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA + && minCapacity <= DEFAULT_CAPACITY)) { + modCount++; + grow(minCapacity); } } /** + * The maximum size of array to allocate (unless necessary). + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private Object[] grow(int minCapacity) { + return elementData = Arrays.copyOf(elementData, + newCapacity(minCapacity)); + } + + private Object[] grow() { + return grow(size + 1); + } + + /** + * Returns a capacity at least as large as the given minimum capacity. + * Returns the current capacity increased by 50% if that suffices. + * Will not return a capacity greater than MAX_ARRAY_SIZE unless + * the given minimum capacity is greater than MAX_ARRAY_SIZE. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private int newCapacity(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity - minCapacity <= 0) { + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) + return Math.max(DEFAULT_CAPACITY, minCapacity); + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return minCapacity; + } + return (newCapacity - MAX_ARRAY_SIZE <= 0) + ? newCapacity + : hugeCapacity(minCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) + ? Integer.MAX_VALUE + : MAX_ARRAY_SIZE; + } + + /** * Returns the number of elements in this list. * * @return the number of elements in this list @@ -197,22 +285,22 @@ public class ArrayList extends Abstra } /** - * Returns true if this list contains no elements. + * Returns {@code true} if this list contains no elements. * - * @return true if this list contains no elements + * @return {@code true} if this list contains no elements */ public boolean isEmpty() { return size == 0; } /** - * Returns true if this list contains the specified element. - * More formally, returns true if and only if this list contains - * at least one element e such that - * (o==null ? e==null : o.equals(e)). + * Returns {@code true} if this list contains the specified element. + * More formally, returns {@code true} if and only if this list contains + * at least one element {@code e} such that + * {@code Objects.equals(o, e)}. * * @param o element whose presence in this list is to be tested - * @return true if this list contains the specified element + * @return {@code true} if this list contains the specified element */ public boolean contains(Object o) { return indexOf(o) >= 0; @@ -221,8 +309,8 @@ public class ArrayList extends Abstra /** * Returns the index of the first occurrence of the specified element * in this list, or -1 if this list does not contain the element. - * More formally, returns the lowest index i such that - * (o==null ? get(i)==null : o.equals(get(i))), + * More formally, returns the lowest index {@code i} such that + * {@code Objects.equals(o, get(i))}, * or -1 if there is no such index. */ public int indexOf(Object o) { @@ -241,8 +329,8 @@ public class ArrayList extends Abstra /** * Returns the index of the last occurrence of the specified element * in this list, or -1 if this list does not contain the element. - * More formally, returns the highest index i such that - * (o==null ? get(i)==null : o.equals(get(i))), + * More formally, returns the highest index {@code i} such that + * {@code Objects.equals(o, get(i))}, * or -1 if there is no such index. */ public int lastIndexOf(Object o) { @@ -259,21 +347,20 @@ public class ArrayList extends Abstra } /** - * Returns a shallow copy of this ArrayList instance. (The + * Returns a shallow copy of this {@code ArrayList} instance. (The * elements themselves are not copied.) * - * @return a clone of this ArrayList instance + * @return a clone of this {@code ArrayList} instance */ public Object clone() { try { - @SuppressWarnings("unchecked") - ArrayList v = (ArrayList) super.clone(); + ArrayList v = (ArrayList) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable - throw new InternalError(); + throw new InternalError(e); } } @@ -306,7 +393,7 @@ public class ArrayList extends Abstra *

If the list fits in the specified array with room to spare * (i.e., the array has more elements than the list), the element in * the array immediately following the end of the collection is set to - * null. (This is useful in determining the length of the + * {@code null}. (This is useful in determining the length of the * list only if the caller knows that the list does not contain * any null elements.) * @@ -345,8 +432,7 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { - rangeCheck(index); - + Objects.checkIndex(index, size); return elementData(index); } @@ -360,22 +446,33 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { - rangeCheck(index); - + Objects.checkIndex(index, size); E oldValue = elementData(index); elementData[index] = element; return oldValue; } /** + * This helper method split out from add(E) to keep method + * bytecode size under 35 (the -XX:MaxInlineSize default value), + * which helps when add(E) is called in a C1-compiled loop. + */ + private void add(E e, Object[] elementData, int s) { + if (s == elementData.length) + elementData = grow(); + elementData[s] = e; + size = s + 1; + } + + /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { - ensureCapacity(size + 1); // Increments modCount!! - elementData[size++] = e; + modCount++; + add(e, elementData, size); return true; } @@ -390,12 +487,16 @@ public class ArrayList extends Abstra */ public void add(int index, E element) { rangeCheckForAdd(index); - - ensureCapacity(size+1); // Increments modCount!! - System.arraycopy(elementData, index, elementData, index + 1, - size - index); + modCount++; + final int s; + Object[] elementData; + if ((s = size) == (elementData = this.elementData).length) + elementData = grow(); + System.arraycopy(elementData, index, + elementData, index + 1, + s - index); elementData[index] = element; - size++; + size = s + 1; } /** @@ -408,7 +509,7 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { - rangeCheck(index); + Objects.checkIndex(index, size); modCount++; E oldValue = elementData(index); @@ -417,7 +518,7 @@ public class ArrayList extends Abstra if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); - elementData[--size] = null; // Let gc do its work + elementData[--size] = null; // clear to let GC do its work return oldValue; } @@ -426,14 +527,14 @@ public class ArrayList extends Abstra * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index - * i such that - * (o==null ? get(i)==null : o.equals(get(i))) - * (if such an element exists). Returns true if this list + * {@code i} such that + * {@code Objects.equals(o, get(i))} + * (if such an element exists). Returns {@code true} if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present - * @return true if this list contained the specified element + * @return {@code true} if this list contained the specified element */ public boolean remove(Object o) { if (o == null) { @@ -462,7 +563,7 @@ public class ArrayList extends Abstra if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); - elementData[--size] = null; // Let gc do its work + elementData[--size] = null; // clear to let GC do its work } /** @@ -472,7 +573,7 @@ public class ArrayList extends Abstra public void clear() { modCount++; - // Let gc do its work + // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; @@ -489,16 +590,22 @@ public class ArrayList extends Abstra * list is nonempty.) * * @param c collection containing elements to be added to this list - * @return true if this list changed as a result of the call + * @return {@code true} if this list changed as a result of the call * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection c) { Object[] a = c.toArray(); + modCount++; int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount - System.arraycopy(a, 0, elementData, size, numNew); - size += numNew; - return numNew != 0; + if (numNew == 0) + return false; + Object[] elementData; + final int s; + if (numNew > (elementData = this.elementData).length - (s = size)) + elementData = grow(s + numNew); + System.arraycopy(a, 0, elementData, s, numNew); + size = s + numNew; + return true; } /** @@ -512,7 +619,7 @@ public class ArrayList extends Abstra * @param index index at which to insert the first element from the * specified collection * @param c collection containing elements to be added to this list - * @return true if this list changed as a result of the call + * @return {@code true} if this list changed as a result of the call * @throws IndexOutOfBoundsException {@inheritDoc} * @throws NullPointerException if the specified collection is null */ @@ -520,17 +627,23 @@ public class ArrayList extends Abstra rangeCheckForAdd(index); Object[] a = c.toArray(); + modCount++; int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount + if (numNew == 0) + return false; + Object[] elementData; + final int s; + if (numNew > (elementData = this.elementData).length - (s = size)) + elementData = grow(s + numNew); - int numMoved = size - index; + int numMoved = s - index; if (numMoved > 0) - System.arraycopy(elementData, index, elementData, index + numNew, + System.arraycopy(elementData, index, + elementData, index + numNew, numMoved); - System.arraycopy(a, 0, elementData, index, numNew); - size += numNew; - return numNew != 0; + size = s + numNew; + return true; } /** @@ -543,31 +656,25 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException if {@code fromIndex} or * {@code toIndex} is out of range * ({@code fromIndex < 0 || - * fromIndex >= size() || * toIndex > size() || * toIndex < fromIndex}) */ protected void removeRange(int fromIndex, int toIndex) { + if (fromIndex > toIndex) { + throw new IndexOutOfBoundsException( + outOfBoundsMsg(fromIndex, toIndex)); + } modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); - // Let gc do its work + // clear to let GC do its work int newSize = size - (toIndex-fromIndex); - while (size != newSize) - elementData[--size] = null; - } - - /** - * Checks if the given index is in range. If not, throws an appropriate - * runtime exception. This method does *not* check if the index is - * negative: It is always used immediately prior to an array access, - * which throws an ArrayIndexOutOfBoundsException if index is negative. - */ - private void rangeCheck(int index) { - if (index >= size) - throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + for (int i = newSize; i < size; i++) { + elementData[i] = null; + } + size = newSize; } /** @@ -588,19 +695,29 @@ public class ArrayList extends Abstra } /** + * A version used in checking (fromIndex > toIndex) condition + */ + private static String outOfBoundsMsg(int fromIndex, int toIndex) { + return "From Index: " + fromIndex + " > To Index: " + toIndex; + } + + /** * Removes from this list all of its elements that are contained in the * specified collection. * * @param c collection containing elements to be removed from this list * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see Collection#contains(Object) */ public boolean removeAll(Collection c) { + Objects.requireNonNull(c); return batchRemove(c, false); } @@ -612,13 +729,16 @@ public class ArrayList extends Abstra * @param c collection containing elements to be retained in this list * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see Collection#contains(Object) */ public boolean retainAll(Collection c) { + Objects.requireNonNull(c); return batchRemove(c, true); } @@ -640,6 +760,7 @@ public class ArrayList extends Abstra w += size - r; } if (w != size) { + // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; @@ -651,12 +772,12 @@ public class ArrayList extends Abstra } /** - * Save the state of the ArrayList instance to a stream (that + * Save the state of the {@code ArrayList} instance to a stream (that * is, serialize it). * - * @serialData The length of the array backing the ArrayList + * @serialData The length of the array backing the {@code ArrayList} * instance is emitted (int), followed by all of its elements - * (each an Object) in the proper order. + * (each an {@code Object}) in the proper order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ @@ -664,35 +785,47 @@ public class ArrayList extends Abstra int expectedModCount = modCount; s.defaultWriteObject(); - // Write out array length - s.writeInt(elementData.length); + // Write out size as capacity for behavioural compatibility with clone() + s.writeInt(size); // Write out all elements in the proper order. - for (int i=0; iArrayList instance from a stream (that is, + * Reconstitute the {@code ArrayList} instance from a stream (that is, * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { + // Read in size, and any hidden stuff s.defaultReadObject(); - // Read in array length and allocate array - int arrayLength = s.readInt(); - Object[] a = elementData = new Object[arrayLength]; - - // Read in all elements in the proper order. - for (int i=0; i 0) { + // like clone(), allocate array based upon size not capacity + Object[] elements = new Object[size]; + + // Read in all elements in the proper order. + for (int i = 0; i < size; i++) { + elements[i] = s.readObject(); + } + + elementData = elements; + } else if (size == 0) { + elementData = EMPTY_ELEMENTDATA; + } else { + throw new java.io.InvalidObjectException("Invalid size: " + size); + } } /** @@ -708,8 +841,7 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public ListIterator listIterator(int index) { - if (index < 0 || index > size) - throw new IndexOutOfBoundsException("Index: "+index); + rangeCheckForAdd(index); return new ListItr(index); } @@ -744,6 +876,9 @@ public class ArrayList extends Abstra int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; + // prevent creating a synthetic constructor + Itr() {} + public boolean hasNext() { return cursor != size; } @@ -776,6 +911,28 @@ public class ArrayList extends Abstra } } + @Override + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + Objects.requireNonNull(consumer); + final int size = ArrayList.this.size; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + consumer.accept((E) elementData[i++]); + } + // update once at end of iteration to reduce heap write traffic + cursor = i; + lastRet = i - 1; + checkForComodification(); + } + final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); @@ -874,76 +1031,75 @@ public class ArrayList extends Abstra */ public List subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); - return new SubList(this, 0, fromIndex, toIndex); + return new SubList<>(this, fromIndex, toIndex); } - static void subListRangeCheck(int fromIndex, int toIndex, int size) { - if (fromIndex < 0) - throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); - if (toIndex > size) - throw new IndexOutOfBoundsException("toIndex = " + toIndex); - if (fromIndex > toIndex) - throw new IllegalArgumentException("fromIndex(" + fromIndex + - ") > toIndex(" + toIndex + ")"); - } - - private class SubList extends AbstractList implements RandomAccess { - private final AbstractList parent; - private final int parentOffset; + private static class SubList extends AbstractList implements RandomAccess { + private final ArrayList root; + private final SubList parent; private final int offset; - int size; + private int size; - SubList(AbstractList parent, - int offset, int fromIndex, int toIndex) { + /** + * Constructs a sublist of an arbitrary ArrayList. + */ + public SubList(ArrayList root, int fromIndex, int toIndex) { + this.root = root; + this.parent = null; + this.offset = fromIndex; + this.size = toIndex - fromIndex; + this.modCount = root.modCount; + } + + /** + * Constructs a sublist of another SubList. + */ + private SubList(SubList parent, int fromIndex, int toIndex) { + this.root = parent.root; this.parent = parent; - this.parentOffset = fromIndex; - this.offset = offset + fromIndex; + this.offset = parent.offset + fromIndex; this.size = toIndex - fromIndex; - this.modCount = ArrayList.this.modCount; + this.modCount = root.modCount; } - public E set(int index, E e) { - rangeCheck(index); + public E set(int index, E element) { + Objects.checkIndex(index, size); checkForComodification(); - E oldValue = ArrayList.this.elementData(offset + index); - ArrayList.this.elementData[offset + index] = e; + E oldValue = root.elementData(offset + index); + root.elementData[offset + index] = element; return oldValue; } public E get(int index) { - rangeCheck(index); + Objects.checkIndex(index, size); checkForComodification(); - return ArrayList.this.elementData(offset + index); + return root.elementData(offset + index); } public int size() { checkForComodification(); - return this.size; + return size; } - public void add(int index, E e) { + public void add(int index, E element) { rangeCheckForAdd(index); checkForComodification(); - parent.add(parentOffset + index, e); - this.modCount = parent.modCount; - this.size++; + root.add(offset + index, element); + updateSizeAndModCount(1); } public E remove(int index) { - rangeCheck(index); + Objects.checkIndex(index, size); checkForComodification(); - E result = parent.remove(parentOffset + index); - this.modCount = parent.modCount; - this.size--; + E result = root.remove(offset + index); + updateSizeAndModCount(-1); return result; } protected void removeRange(int fromIndex, int toIndex) { checkForComodification(); - parent.removeRange(parentOffset + fromIndex, - parentOffset + toIndex); - this.modCount = parent.modCount; - this.size -= toIndex - fromIndex; + root.removeRange(offset + fromIndex, offset + toIndex); + updateSizeAndModCount(fromIndex - toIndex); } public boolean addAll(Collection c) { @@ -955,11 +1111,9 @@ public class ArrayList extends Abstra int cSize = c.size(); if (cSize==0) return false; - checkForComodification(); - parent.addAll(parentOffset + index, c); - this.modCount = parent.modCount; - this.size += cSize; + root.addAll(offset + index, c); + updateSizeAndModCount(cSize); return true; } @@ -967,15 +1121,14 @@ public class ArrayList extends Abstra return listIterator(); } - public ListIterator listIterator(final int index) { + public ListIterator listIterator(int index) { checkForComodification(); rangeCheckForAdd(index); - final int offset = this.offset; return new ListIterator() { int cursor = index; int lastRet = -1; - int expectedModCount = ArrayList.this.modCount; + int expectedModCount = root.modCount; public boolean hasNext() { return cursor != SubList.this.size; @@ -987,7 +1140,7 @@ public class ArrayList extends Abstra int i = cursor; if (i >= SubList.this.size) throw new NoSuchElementException(); - Object[] elementData = ArrayList.this.elementData; + Object[] elementData = root.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; @@ -1004,13 +1157,33 @@ public class ArrayList extends Abstra int i = cursor - 1; if (i < 0) throw new NoSuchElementException(); - Object[] elementData = ArrayList.this.elementData; + Object[] elementData = root.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i; return (E) elementData[offset + (lastRet = i)]; } + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + Objects.requireNonNull(consumer); + final int size = SubList.this.size; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = root.elementData; + if (offset + i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + consumer.accept((E) elementData[offset + (i++)]); + } + // update once at end of iteration to reduce heap write traffic + lastRet = cursor = i; + checkForComodification(); + } + public int nextIndex() { return cursor; } @@ -1028,7 +1201,7 @@ public class ArrayList extends Abstra SubList.this.remove(lastRet); cursor = lastRet; lastRet = -1; - expectedModCount = ArrayList.this.modCount; + expectedModCount = root.modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } @@ -1040,7 +1213,7 @@ public class ArrayList extends Abstra checkForComodification(); try { - ArrayList.this.set(offset + lastRet, e); + root.set(offset + lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } @@ -1054,14 +1227,14 @@ public class ArrayList extends Abstra SubList.this.add(i, e); cursor = i + 1; lastRet = -1; - expectedModCount = ArrayList.this.modCount; + expectedModCount = root.modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { - if (expectedModCount != ArrayList.this.modCount) + if (root.modCount != expectedModCount) throw new ConcurrentModificationException(); } }; @@ -1069,12 +1242,7 @@ public class ArrayList extends Abstra public List subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); - return new SubList(this, offset, fromIndex, toIndex); - } - - private void rangeCheck(int index) { - if (index < 0 || index >= this.size) - throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + return new SubList<>(this, fromIndex, toIndex); } private void rangeCheckForAdd(int index) { @@ -1087,8 +1255,303 @@ public class ArrayList extends Abstra } private void checkForComodification() { - if (ArrayList.this.modCount != this.modCount) + if (root.modCount != modCount) throw new ConcurrentModificationException(); } + + private void updateSizeAndModCount(int sizeChange) { + SubList slist = this; + do { + slist.size += sizeChange; + slist.modCount = root.modCount; + slist = slist.parent; + } while (slist != null); + } + + public Spliterator spliterator() { + checkForComodification(); + + // ArrayListSpliterator is not used because late-binding logic + // is different here + return new Spliterator<>() { + private int index = offset; // current index, modified on advance/split + private int fence = -1; // -1 until used; then one past last index + private int expectedModCount; // initialized when fence set + + private int getFence() { // initialize fence to size on first use + int hi; // (a specialized variant appears in method forEach) + if ((hi = fence) < 0) { + expectedModCount = modCount; + hi = fence = offset + size; + } + return hi; + } + + public ArrayListSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + // ArrayListSpliterator could be used here as the source is already bound + return (lo >= mid) ? null : // divide range in half unless too small + new ArrayListSpliterator<>(root, lo, index = mid, + expectedModCount); + } + + public boolean tryAdvance(Consumer action) { + Objects.requireNonNull(action); + int hi = getFence(), i = index; + if (i < hi) { + index = i + 1; + @SuppressWarnings("unchecked") E e = (E)root.elementData[i]; + action.accept(e); + if (root.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + int i, hi, mc; // hoist accesses and checks from loop + ArrayList lst = root; + Object[] a; + if ((a = lst.elementData) != null) { + if ((hi = fence) < 0) { + mc = modCount; + hi = offset + size; + } + else + mc = expectedModCount; + if ((i = index) >= 0 && (index = hi) <= a.length) { + for (; i < hi; ++i) { + @SuppressWarnings("unchecked") E e = (E) a[i]; + action.accept(e); + } + if (lst.modCount == mc) + return; + } + } + throw new ConcurrentModificationException(); + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + }; + } + } + + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + final int expectedModCount = modCount; + @SuppressWarnings("unchecked") + final E[] elementData = (E[]) this.elementData; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + action.accept(elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + /** + * Creates a late-binding + * and fail-fast {@link Spliterator} over the elements in this + * list. + * + *

The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. + * Overriding implementations should document the reporting of additional + * characteristic values. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ + @Override + public Spliterator spliterator() { + return new ArrayListSpliterator<>(this, 0, -1, 0); + } + + /** Index-based split-by-two, lazily initialized Spliterator */ + static final class ArrayListSpliterator implements Spliterator { + + /* + * If ArrayLists were immutable, or structurally immutable (no + * adds, removes, etc), we could implement their spliterators + * with Arrays.spliterator. Instead we detect as much + * interference during traversal as practical without + * sacrificing much performance. We rely primarily on + * modCounts. These are not guaranteed to detect concurrency + * violations, and are sometimes overly conservative about + * within-thread interference, but detect enough problems to + * be worthwhile in practice. To carry this out, we (1) lazily + * initialize fence and expectedModCount until the latest + * point that we need to commit to the state we are checking + * against; thus improving precision. (This doesn't apply to + * SubLists, that create spliterators with current non-lazy + * values). (2) We perform only a single + * ConcurrentModificationException check at the end of forEach + * (the most performance-sensitive method). When using forEach + * (as opposed to iterators), we can normally only detect + * interference after actions, not before. Further + * CME-triggering checks apply to all other possible + * violations of assumptions for example null or too-small + * elementData array given its size(), that could only have + * occurred due to interference. This allows the inner loop + * of forEach to run without any further checks, and + * simplifies lambda-resolution. While this does entail a + * number of checks, note that in the common case of + * list.stream().forEach(a), no checks or other computation + * occur anywhere other than inside forEach itself. The other + * less-often-used methods cannot take advantage of most of + * these streamlinings. + */ + + private final ArrayList list; + private int index; // current index, modified on advance/split + private int fence; // -1 until used; then one past last index + private int expectedModCount; // initialized when fence set + + /** Create new spliterator covering the given range */ + ArrayListSpliterator(ArrayList list, int origin, int fence, + int expectedModCount) { + this.list = list; // OK if null unless traversed + this.index = origin; + this.fence = fence; + this.expectedModCount = expectedModCount; + } + + private int getFence() { // initialize fence to size on first use + int hi; // (a specialized variant appears in method forEach) + ArrayList lst; + if ((hi = fence) < 0) { + if ((lst = list) == null) + hi = fence = 0; + else { + expectedModCount = lst.modCount; + hi = fence = lst.size; + } + } + return hi; + } + + public ArrayListSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : // divide range in half unless too small + new ArrayListSpliterator<>(list, lo, index = mid, + expectedModCount); + } + + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + int hi = getFence(), i = index; + if (i < hi) { + index = i + 1; + @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; + action.accept(e); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; // hoist accesses and checks from loop + ArrayList lst; Object[] a; + if (action == null) + throw new NullPointerException(); + if ((lst = list) != null && (a = lst.elementData) != null) { + if ((hi = fence) < 0) { + mc = lst.modCount; + hi = lst.size; + } + else + mc = expectedModCount; + if ((i = index) >= 0 && (index = hi) <= a.length) { + for (; i < hi; ++i) { + @SuppressWarnings("unchecked") E e = (E) a[i]; + action.accept(e); + } + if (lst.modCount == mc) + return; + } + } + throw new ConcurrentModificationException(); + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + final int expectedModCount = modCount; + final Object[] elementData = this.elementData; + int r = 0, w = 0, remaining = size, deleted = 0; + try { + for (; remaining > 0; remaining--, r++) { + @SuppressWarnings("unchecked") E e = (E) elementData[r]; + if (filter.test(e)) + deleted++; + else { + if (r != w) + elementData[w] = e; + w++; + } + } + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + return deleted > 0; + } catch (Throwable ex) { + for (; remaining > 0; remaining--, r++, w++) + elementData[w] = elementData[r]; + throw ex; + } finally { + if (deleted > 0) { + modCount++; + size -= deleted; + while (--deleted >= 0) + elementData[w++] = null; + } + } + } + + @Override + @SuppressWarnings("unchecked") + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.apply((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + @SuppressWarnings("unchecked") + public void sort(Comparator c) { + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, size, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; } }