--- jsr166/src/main/java/util/ArrayList.java 2005/11/26 04:33:04 1.5 +++ jsr166/src/main/java/util/ArrayList.java 2018/11/11 16:27:28 1.65 @@ -1,41 +1,63 @@ /* - * %W% %E% + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. - * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * 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 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. */ package java.util; -import java.util.*; // for javadoc (till 6280605 is fixed) + +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +// OPENJDK import jdk.internal.access.SharedSecrets; /** - * 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.

+ * 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 @@ -49,47 +71,69 @@ import java.util.*; // for javadoc (till * unsynchronized access to the list:

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

The iterators returned by this class's iterator and - * listIterator methods are fail-fast: if the list is - * structurally modified at any time after the iterator is created, in any way - * except through the iterator's own remove or add methods, - * the iterator will throw a {@link ConcurrentModificationException}. Thus, in - * the face of concurrent modification, the iterator fails quickly and cleanly, - * rather than risking arbitrary, non-deterministic behavior at an undetermined - * time in the future.

+ *

+ * 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 + * created, in any way except through the iterator's own + * {@link ListIterator#remove() remove} or + * {@link ListIterator#add(Object) add} methods, the iterator will throw a + * {@link ConcurrentModificationException}. Thus, in the face of + * concurrent modification, the iterator fails quickly and cleanly, rather + * than risking arbitrary, non-deterministic behavior at an undetermined + * time in the future. * - * Note that the fail-fast behavior of an iterator cannot be guaranteed + *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators - * throw ConcurrentModificationException on a best-effort basis. + * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this - * exception for its correctness: the fail-fast behavior of iterators - * should be used only to detect bugs.

+ * exception for its correctness: the fail-fast behavior of iterators + * should be used only to detect bugs. * - * This class is a member of the - * + *

This class is a member of the + * * Java Collections Framework. * + * @param the type of elements in this list + * * @author Josh Bloch * @author Neal Gafter - * @version %I%, %G% - * @see Collection - * @see List - * @see LinkedList - * @see Vector + * @see Collection + * @see List + * @see LinkedList + * @see Vector * @since 1.2 */ - public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { 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). @@ -101,102 +145,134 @@ public class ArrayList extends Abstra /** * Constructs an empty list with the specified initial capacity. * - * @param initialCapacity the initial capacity of the list + * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * 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; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's - * iterator. The ArrayList instance has an initial capacity of - * 110% the size of the specified collection. + * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection c) { - int size = c.size(); - // 10% for growth - int cap = ((size/10)+1)*11; - if (cap > 0) { - Object[] a = new Object[cap]; - a[size] = a[size+1] = UNALLOCATED; - Object[] b = c.toArray(a); - if (b[size] == null && b[size+1] == UNALLOCATED) { - b[size+1] = null; - elementData = b; - this.size = size; - return; - } - } - initFromConcurrentlyMutating(c); - } - - private void initFromConcurrentlyMutating(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); + elementData = c.toArray(); + 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; + } } - private final static Object UNALLOCATED = new Object(); - /** - * 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); - } + modCount++; + 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 */ public void ensureCapacity(int minCapacity) { - modCount++; - if (minCapacity > elementData.length) - growArray(minCapacity); + if (minCapacity > elementData.length + && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA + && minCapacity <= DEFAULT_CAPACITY)) { + modCount++; + grow(minCapacity); + } } /** - * Increases the capacity of the array. + * 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 void growArray(int minCapacity) { - int oldCapacity = elementData.length; - // Double size if small; else grow by 50% - int newCapacity = ((oldCapacity < 64)? - (oldCapacity * 2): - ((oldCapacity * 3)/2 + 1)); - if (newCapacity < minCapacity) - newCapacity = minCapacity; - elementData = Arrays.copyOf(elementData, newCapacity); + 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; } /** @@ -205,87 +281,105 @@ public class ArrayList extends Abstra * @return the number of elements in this list */ public int size() { - return size; + return size; } /** - * 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; + 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; + return indexOf(o) >= 0; } /** * 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) { - if (o == null) { - for (int i = 0; i < size; i++) - if (elementData[i]==null) - return i; - } else { - for (int i = 0; i < size; i++) - if (o.equals(elementData[i])) - return i; - } - return -1; + return indexOfRange(o, 0, size); + } + + int indexOfRange(Object o, int start, int end) { + Object[] es = elementData; + if (o == null) { + for (int i = start; i < end; i++) { + if (es[i] == null) { + return i; + } + } + } else { + for (int i = start; i < end; i++) { + if (o.equals(es[i])) { + return i; + } + } + } + return -1; } /** * 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) { - if (o == null) { - for (int i = size-1; i >= 0; i--) - if (elementData[i]==null) - return i; - } else { - for (int i = size-1; i >= 0; i--) - if (o.equals(elementData[i])) - return i; - } - return -1; + return lastIndexOfRange(o, 0, size); + } + + int lastIndexOfRange(Object o, int start, int end) { + Object[] es = elementData; + if (o == null) { + for (int i = end - 1; i >= start; i--) { + if (es[i] == null) { + return i; + } + } + } else { + for (int i = end - 1; i >= start; i--) { + if (o.equals(es[i])) { + return i; + } + } + } + return -1; } /** - * 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 { - 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(); - } + try { + 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(e); + } } /** @@ -317,7 +411,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.) * @@ -330,11 +424,12 @@ public class ArrayList extends Abstra * this list * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); - System.arraycopy(elementData, 0, a, 0, size); + System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; @@ -342,11 +437,14 @@ public class ArrayList extends Abstra // Positional Access Operations - /** - * Create and return an appropriate exception for indexing errors - */ - private static IndexOutOfBoundsException rangeException(int i, int s) { - return new IndexOutOfBoundsException("Index: " + i + ", Size: " + s); + @SuppressWarnings("unchecked") + E elementData(int index) { + return (E) elementData[index]; + } + + @SuppressWarnings("unchecked") + static E elementAt(Object[] es, int index) { + return (E) es[index]; } /** @@ -357,9 +455,8 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { - if (index >= size) - throw rangeException(index, size); - return (E)elementData[index]; + Objects.checkIndex(index, size); + return elementData(index); } /** @@ -372,27 +469,34 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { - if (index >= size) - throw rangeException(index, size); + Objects.checkIndex(index, size); + E oldValue = elementData(index); + elementData[index] = element; + return oldValue; + } - E oldValue = (E) 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) { - ++modCount; - int s = size++; - if (s >= elementData.length) - growArray(s + 1); - elementData[s] = e; - return true; + modCount++; + add(e, elementData, size); + return true; } /** @@ -405,16 +509,18 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { - int s = size; - if (index > s || index < 0) - throw rangeException(index, s); - ++modCount; + rangeCheckForAdd(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 = s + 1; - if (s >= elementData.length) - growArray(s + 1); - System.arraycopy(elementData, index, elementData, index + 1, - s - index); - elementData[index] = element; + // checkInvariants(); } /** @@ -427,61 +533,146 @@ public class ArrayList extends Abstra * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { - int s = size - 1; - if (index > s) - throw rangeException(index, size); - size = s; - modCount++; - Object oldValue = elementData[index]; - int numMoved = s - index; - if (numMoved > 0) - System.arraycopy(elementData, index+1, elementData, index, - numMoved); - elementData[s] = null; // forget removed element - return (E)oldValue; + Objects.checkIndex(index, size); + final Object[] es = elementData; + + @SuppressWarnings("unchecked") E oldValue = (E) es[index]; + fastRemove(es, index); + + // checkInvariants(); + return oldValue; + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof List)) { + return false; + } + + final int expectedModCount = modCount; + // ArrayList can be subclassed and given arbitrary behavior, but we can + // still deal with the common case where o is ArrayList precisely + boolean equal = (o.getClass() == ArrayList.class) + ? equalsArrayList((ArrayList) o) + : equalsRange((List) o, 0, size); + + checkForComodification(expectedModCount); + return equal; + } + + boolean equalsRange(List other, int from, int to) { + final Object[] es = elementData; + if (to > es.length) { + throw new ConcurrentModificationException(); + } + Iterator oit = other.iterator(); + for (; from < to; from++) { + if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) { + return false; + } + } + return !oit.hasNext(); + } + + private boolean equalsArrayList(ArrayList other) { + final int otherModCount = other.modCount; + final int s = size; + boolean equal; + if (equal = (s == other.size)) { + final Object[] otherEs = other.elementData; + final Object[] es = elementData; + if (s > es.length || s > otherEs.length) { + throw new ConcurrentModificationException(); + } + for (int i = 0; i < s; i++) { + if (!Objects.equals(es[i], otherEs[i])) { + equal = false; + break; + } + } + } + other.checkForComodification(otherModCount); + return equal; + } + + private void checkForComodification(final int expectedModCount) { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + /** + * {@inheritDoc} + */ + public int hashCode() { + int expectedModCount = modCount; + int hash = hashCodeRange(0, size); + checkForComodification(expectedModCount); + return hash; + } + + int hashCodeRange(int from, int to) { + final Object[] es = elementData; + if (to > es.length) { + throw new ConcurrentModificationException(); + } + int hashCode = 1; + for (int i = from; i < to; i++) { + Object e = es[i]; + hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); + } + return hashCode; } /** * 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) { - for (int index = 0; index < size; index++) - if (elementData[index] == null) { - fastRemove(index); - return true; - } - } else { - for (int index = 0; index < size; index++) - if (o.equals(elementData[index])) { - fastRemove(index); - return true; - } + final Object[] es = elementData; + final int size = this.size; + int i = 0; + found: { + if (o == null) { + for (; i < size; i++) + if (es[i] == null) + break found; + } else { + for (; i < size; i++) + if (o.equals(es[i])) + break found; + } + return false; } - return false; + fastRemove(es, i); + return true; } - /* + /** * Private remove method that skips bounds checking and does not * return the value removed. */ - private void fastRemove(int index) { + private void fastRemove(Object[] es, int i) { modCount++; - int numMoved = size - index - 1; - if (numMoved > 0) - System.arraycopy(elementData, index+1, elementData, index, - numMoved); - elementData[--size] = null; // Let gc do its work + final int newSize; + if ((newSize = size - 1) > i) + System.arraycopy(es, i + 1, es, i, newSize - i); + es[size = newSize] = null; } /** @@ -489,13 +680,10 @@ public class ArrayList extends Abstra * be empty after this call returns. */ public void clear() { - modCount++; - - // Let gc do its work - for (int i = 0; i < size; i++) - elementData[i] = null; - - size = 0; + modCount++; + final Object[] es = elementData; + for (int to = size, i = size = 0; i < to; i++) + es[i] = null; } /** @@ -508,16 +696,23 @@ 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(); + 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; + // checkInvariants(); + return true; } /** @@ -531,231 +726,1043 @@ 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 */ public boolean addAll(int index, Collection c) { - if (index > size || index < 0) - throw new IndexOutOfBoundsException( - "Index: " + index + ", Size: " + size); - - Object[] a = c.toArray(); - int numNew = a.length; - ensureCapacity(size + numNew); // Increments modCount - - int numMoved = size - index; - if (numMoved > 0) - System.arraycopy(elementData, index, elementData, index + numNew, - numMoved); + rangeCheckForAdd(index); + + Object[] a = c.toArray(); + modCount++; + int numNew = a.length; + if (numNew == 0) + return false; + Object[] elementData; + final int s; + if (numNew > (elementData = this.elementData).length - (s = size)) + elementData = grow(s + numNew); + int numMoved = s - index; + if (numMoved > 0) + System.arraycopy(elementData, index, + elementData, index + numNew, + numMoved); System.arraycopy(a, 0, elementData, index, numNew); - size += numNew; - return numNew != 0; + size = s + numNew; + // checkInvariants(); + return true; } /** * Removes from this list all of the elements whose index is between - * fromIndex, inclusive, and toIndex, exclusive. + * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. * Shifts any succeeding elements to the left (reduces their index). - * This call shortens the list by (toIndex - fromIndex) elements. - * (If toIndex==fromIndex, this operation has no effect.) + * This call shortens the list by {@code (toIndex - fromIndex)} elements. + * (If {@code toIndex==fromIndex}, this operation has no effect.) * - * @param fromIndex index of first element to be removed - * @param toIndex index after last element to be removed - * @throws IndexOutOfBoundsException if fromIndex or toIndex out of - * range (fromIndex < 0 || fromIndex >= size() || toIndex - * > size() || toIndex < fromIndex) + * @throws IndexOutOfBoundsException if {@code fromIndex} or + * {@code toIndex} is out of range + * ({@code fromIndex < 0 || + * toIndex > size() || + * toIndex < fromIndex}) */ protected void removeRange(int fromIndex, int toIndex) { - modCount++; - int numMoved = size - toIndex; - System.arraycopy(elementData, toIndex, elementData, fromIndex, - numMoved); + if (fromIndex > toIndex) { + throw new IndexOutOfBoundsException( + outOfBoundsMsg(fromIndex, toIndex)); + } + modCount++; + shiftTailOverGap(elementData, fromIndex, toIndex); + // checkInvariants(); + } - // Let gc do its work - int newSize = size - (toIndex-fromIndex); - while (size != newSize) - elementData[--size] = null; + /** Erases the gap from lo to hi, by sliding down following elements. */ + private void shiftTailOverGap(Object[] es, int lo, int hi) { + System.arraycopy(es, hi, es, lo, size - hi); + for (int to = size, i = (size -= hi - lo); i < to; i++) + es[i] = null; } /** - * Save the state of the ArrayList instance to a stream (that - * is, serialize it). + * A version of rangeCheck used by add and addAll. + */ + private void rangeCheckForAdd(int index) { + if (index > size || index < 0) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + /** + * Constructs an IndexOutOfBoundsException detail message. + * Of the many possible refactorings of the error handling code, + * this "outlining" performs best with both server and client VMs. + */ + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+size; + } + + /** + * 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) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements + * (optional), + * or if the specified collection is null + * @see Collection#contains(Object) + */ + public boolean removeAll(Collection c) { + return batchRemove(c, false, 0, size); + } + + /** + * Retains only the elements in this list that are contained in the + * specified collection. In other words, removes from this list all + * of its elements that are not contained in the specified collection. * - * @serialData The length of the array backing the ArrayList + * @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) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements + * (optional), + * or if the specified collection is null + * @see Collection#contains(Object) + */ + public boolean retainAll(Collection c) { + return batchRemove(c, true, 0, size); + } + + boolean batchRemove(Collection c, boolean complement, + final int from, final int end) { + Objects.requireNonNull(c); + final Object[] es = elementData; + int r; + // Optimize for initial run of survivors + for (r = from;; r++) { + if (r == end) + return false; + if (c.contains(es[r]) != complement) + break; + } + int w = r++; + try { + for (Object e; r < end; r++) + if (c.contains(e = es[r]) == complement) + es[w++] = e; + } catch (Throwable ex) { + // Preserve behavioral compatibility with AbstractCollection, + // even if c.contains() throws. + System.arraycopy(es, r, es, w, end - r); + w += end - r; + throw ex; + } finally { + modCount += end - w; + shiftTailOverGap(es, w, end); + } + // checkInvariants(); + return true; + } + + /** + * Saves the state of the {@code ArrayList} instance to a stream + * (that is, serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @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{ - // Write out element count, and any hidden stuff - int expectedModCount = modCount; - s.defaultWriteObject(); + throws java.io.IOException { + // Write out element count, and any hidden stuff + int expectedModCount = modCount; + s.defaultWriteObject(); - // Write out array length - s.writeInt(elementData.length); + // Write out size as capacity for behavioral 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, - * deserialize it). + * Reconstitutes the {@code ArrayList} instance from a stream (that is, + * deserializes it). + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs */ 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 size, and any hidden stuff + s.defaultReadObject(); - // Read in all elements in the proper order. - for (int i=0; i 0) { + // like clone(), allocate array based upon size not capacity + jsr166.Platform.checkArray(s, Object[].class, size); + 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); + } + } /** - * Returns a list-iterator of the elements in this list (in proper + * Returns a list iterator over the elements in this list (in proper * sequence), starting at the specified position in the list. - * Obeys the general contract of List.listIterator(int).

+ * The specified index indicates the first element that would be + * returned by an initial call to {@link ListIterator#next next}. + * An initial call to {@link ListIterator#previous previous} would + * return the element with the specified index minus one. + * + *

The returned list iterator is fail-fast. * - * The list-iterator is fail-fast: if the list is structurally - * modified at any time after the Iterator is created, in any way except - * through the list-iterator's own remove or add - * methods, the list-iterator will throw a - * ConcurrentModificationException. Thus, in the face of - * concurrent modification, the iterator fails quickly and cleanly, rather - * than risking arbitrary, non-deterministic behavior at an undetermined - * time in the future. - * - * @param index index of the first element to be returned from the - * list-iterator (by a call to next) - * @return a ListIterator of the elements in this list (in proper - * sequence), starting at the specified position in the list * @throws IndexOutOfBoundsException {@inheritDoc} - * @see List#listIterator(int) */ public ListIterator listIterator(int index) { - if (index < 0 || index > size) - throw new IndexOutOfBoundsException("Index: "+index); - return new ArrayListIterator(index); + rangeCheckForAdd(index); + return new ListItr(index); + } + + /** + * Returns a list iterator over the elements in this list (in proper + * sequence). + * + *

The returned list iterator is fail-fast. + * + * @see #listIterator(int) + */ + public ListIterator listIterator() { + return new ListItr(0); } /** * Returns an iterator over the elements in this list in proper sequence. * + *

The returned iterator is fail-fast. + * * @return an iterator over the elements in this list in proper sequence */ public Iterator iterator() { - return new ArrayListIterator(0); + return new Itr(); } /** - * A streamlined version of AbstractList.Itr + * An optimized version of AbstractList.Itr */ - final class ArrayListIterator implements ListIterator { - int cursor; // index of next element to return; - int lastRet; // index of last element, or -1 if no such - int expectedModCount; // to check for CME + private class Itr implements Iterator { + int cursor; // index of next element to return + 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; + } - ArrayListIterator(int index) { - cursor = index; - lastRet = -1; - expectedModCount = modCount; - } + @SuppressWarnings("unchecked") + public E next() { + checkForComodification(); + int i = cursor; + if (i >= size) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i + 1; + return (E) elementData[lastRet = i]; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + ArrayList.this.remove(lastRet); + cursor = lastRet; + lastRet = -1; + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + @Override + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + final int size = ArrayList.this.size; + int i = cursor; + if (i < size) { + final Object[] es = elementData; + if (i >= es.length) + throw new ConcurrentModificationException(); + for (; i < size && modCount == expectedModCount; i++) + action.accept(elementAt(es, i)); + // update once at end to reduce heap write traffic + cursor = i; + lastRet = i - 1; + checkForComodification(); + } + } + + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } - public boolean hasNext() { - return cursor < size; - } + /** + * An optimized version of AbstractList.ListItr + */ + private class ListItr extends Itr implements ListIterator { + ListItr(int index) { + super(); + cursor = index; + } - public boolean hasPrevious() { - return cursor > 0; - } + public boolean hasPrevious() { + return cursor != 0; + } - public int nextIndex() { - return cursor; - } + public int nextIndex() { + return cursor; + } - public int previousIndex() { - return cursor - 1; - } + public int previousIndex() { + return cursor - 1; + } - public E next() { - if (expectedModCount == modCount) { + @SuppressWarnings("unchecked") + public E previous() { + checkForComodification(); + int i = cursor - 1; + if (i < 0) + throw new NoSuchElementException(); + Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i; + return (E) elementData[lastRet = i]; + } + + public void set(E e) { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + ArrayList.this.set(lastRet, e); + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + public void add(E e) { + checkForComodification(); + + try { int i = cursor; - if (i < size) { + ArrayList.this.add(i, e); + cursor = i + 1; + lastRet = -1; + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + } + + /** + * Returns a view of the portion of this list between the specified + * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. (If + * {@code fromIndex} and {@code toIndex} are equal, the returned list is + * empty.) The returned list is backed by this list, so non-structural + * changes in the returned list are reflected in this list, and vice-versa. + * The returned list supports all of the optional list operations. + * + *

This method eliminates the need for explicit range operations (of + * the sort that commonly exist for arrays). Any operation that expects + * a list can be used as a range operation by passing a subList view + * instead of a whole list. For example, the following idiom + * removes a range of elements from a list: + *

+     *      list.subList(from, to).clear();
+     * 
+ * Similar idioms may be constructed for {@link #indexOf(Object)} and + * {@link #lastIndexOf(Object)}, and all of the algorithms in the + * {@link Collections} class can be applied to a subList. + * + *

The semantics of the list returned by this method become undefined if + * the backing list (i.e., this list) is structurally modified in + * any way other than via the returned list. (Structural modifications are + * those that change the size of this list, or otherwise perturb it in such + * a fashion that iterations in progress may yield incorrect results.) + * + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public List subList(int fromIndex, int toIndex) { + subListRangeCheck(fromIndex, toIndex, size); + return new SubList<>(this, fromIndex, toIndex); + } + + private static class SubList extends AbstractList implements RandomAccess { + private final ArrayList root; + private final SubList parent; + private final int offset; + private int size; + + /** + * 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.offset = parent.offset + fromIndex; + this.size = toIndex - fromIndex; + this.modCount = root.modCount; + } + + public E set(int index, E element) { + Objects.checkIndex(index, size); + checkForComodification(); + E oldValue = root.elementData(offset + index); + root.elementData[offset + index] = element; + return oldValue; + } + + public E get(int index) { + Objects.checkIndex(index, size); + checkForComodification(); + return root.elementData(offset + index); + } + + public int size() { + checkForComodification(); + return size; + } + + public void add(int index, E element) { + rangeCheckForAdd(index); + checkForComodification(); + root.add(offset + index, element); + updateSizeAndModCount(1); + } + + public E remove(int index) { + Objects.checkIndex(index, size); + checkForComodification(); + E result = root.remove(offset + index); + updateSizeAndModCount(-1); + return result; + } + + protected void removeRange(int fromIndex, int toIndex) { + checkForComodification(); + root.removeRange(offset + fromIndex, offset + toIndex); + updateSizeAndModCount(fromIndex - toIndex); + } + + public boolean addAll(Collection c) { + return addAll(this.size, c); + } + + public boolean addAll(int index, Collection c) { + rangeCheckForAdd(index); + int cSize = c.size(); + if (cSize==0) + return false; + checkForComodification(); + root.addAll(offset + index, c); + updateSizeAndModCount(cSize); + return true; + } + + public void replaceAll(UnaryOperator operator) { + root.replaceAllRange(operator, offset, offset + size); + } + + public boolean removeAll(Collection c) { + return batchRemove(c, false); + } + + public boolean retainAll(Collection c) { + return batchRemove(c, true); + } + + private boolean batchRemove(Collection c, boolean complement) { + checkForComodification(); + int oldSize = root.size; + boolean modified = + root.batchRemove(c, complement, offset, offset + size); + if (modified) + updateSizeAndModCount(root.size - oldSize); + return modified; + } + + public boolean removeIf(Predicate filter) { + checkForComodification(); + int oldSize = root.size; + boolean modified = root.removeIf(filter, offset, offset + size); + if (modified) + updateSizeAndModCount(root.size - oldSize); + return modified; + } + + public Object[] toArray() { + checkForComodification(); + return Arrays.copyOfRange(root.elementData, offset, offset + size); + } + + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + checkForComodification(); + if (a.length < size) + return (T[]) Arrays.copyOfRange( + root.elementData, offset, offset + size, a.getClass()); + System.arraycopy(root.elementData, offset, a, 0, size); + if (a.length > size) + a[size] = null; + return a; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof List)) { + return false; + } + + boolean equal = root.equalsRange((List)o, offset, offset + size); + checkForComodification(); + return equal; + } + + public int hashCode() { + int hash = root.hashCodeRange(offset, offset + size); + checkForComodification(); + return hash; + } + + public int indexOf(Object o) { + int index = root.indexOfRange(o, offset, offset + size); + checkForComodification(); + return index >= 0 ? index - offset : -1; + } + + public int lastIndexOf(Object o) { + int index = root.lastIndexOfRange(o, offset, offset + size); + checkForComodification(); + return index >= 0 ? index - offset : -1; + } + + public boolean contains(Object o) { + return indexOf(o) >= 0; + } + + public Iterator iterator() { + return listIterator(); + } + + public ListIterator listIterator(int index) { + checkForComodification(); + rangeCheckForAdd(index); + + return new ListIterator() { + int cursor = index; + int lastRet = -1; + int expectedModCount = root.modCount; + + public boolean hasNext() { + return cursor != SubList.this.size; + } + + @SuppressWarnings("unchecked") + public E next() { + checkForComodification(); + int i = cursor; + if (i >= SubList.this.size) + throw new NoSuchElementException(); + Object[] elementData = root.elementData; + if (offset + i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i + 1; + return (E) elementData[offset + (lastRet = i)]; + } + + public boolean hasPrevious() { + return cursor != 0; + } + + @SuppressWarnings("unchecked") + public E previous() { + checkForComodification(); + int i = cursor - 1; + if (i < 0) + throw new NoSuchElementException(); + Object[] elementData = root.elementData; + if (offset + i >= elementData.length) + throw new ConcurrentModificationException(); + cursor = i; + return (E) elementData[offset + (lastRet = i)]; + } + + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + final int size = SubList.this.size; + int i = cursor; + if (i < size) { + final Object[] es = root.elementData; + if (offset + i >= es.length) + throw new ConcurrentModificationException(); + for (; i < size && modCount == expectedModCount; i++) + action.accept(elementAt(es, offset + i)); + // update once at end to reduce heap write traffic + cursor = i; + lastRet = i - 1; + checkForComodification(); + } + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + SubList.this.remove(lastRet); + cursor = lastRet; + lastRet = -1; + expectedModCount = root.modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + public void set(E e) { + if (lastRet < 0) + throw new IllegalStateException(); + checkForComodification(); + + try { + root.set(offset + lastRet, e); + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + + public void add(E e) { + checkForComodification(); + try { - E e = (E)elementData[i]; - lastRet = i; + int i = cursor; + SubList.this.add(i, e); cursor = i + 1; - return e; - } catch (IndexOutOfBoundsException fallthrough) { + lastRet = -1; + expectedModCount = root.modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); } } - } - // Prefer reporting CME if applicable on failures - if (expectedModCount == modCount) - throw new NoSuchElementException(); + + final void checkForComodification() { + if (root.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + }; + } + + public List subList(int fromIndex, int toIndex) { + subListRangeCheck(fromIndex, toIndex, size); + return new SubList<>(this, fromIndex, toIndex); + } + + private void rangeCheckForAdd(int index) { + if (index < 0 || index > this.size) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+this.size; + } + + private void checkForComodification() { + 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 not used here due to late-binding + 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 ArrayList.ArrayListSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + // ArrayListSpliterator can be used here as the source is already bound + return (lo >= mid) ? null : // divide range in half unless too small + root.new ArrayListSpliterator(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 getFence() - index; + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + }; + } + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + final int expectedModCount = modCount; + final Object[] es = elementData; + final int size = this.size; + for (int i = 0; modCount == expectedModCount && i < size; i++) + action.accept(elementAt(es, i)); + if (modCount != expectedModCount) throw new ConcurrentModificationException(); - } + } - public E previous() { - if (expectedModCount == modCount) { - int i = cursor - 1; - if (i < size) { - try { - E e = (E)elementData[i]; - lastRet = i; - cursor = i; - return e; - } catch (IndexOutOfBoundsException fallthrough) { + /** + * 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(0, -1, 0); + } + + /** Index-based split-by-two, lazily initialized Spliterator */ + 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 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 + + /** Creates new spliterator covering the given range. */ + ArrayListSpliterator(int origin, int fence, int expectedModCount) { + 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) + if ((hi = fence) < 0) { + expectedModCount = modCount; + hi = fence = 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(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)elementData[i]; + action.accept(e); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; // hoist accesses and checks from loop + Object[] a; + if (action == null) + throw new NullPointerException(); + if ((a = elementData) != null) { + if ((hi = fence) < 0) { + mc = modCount; + hi = 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 (modCount == mc) + return; } } - if (expectedModCount == modCount) - throw new NoSuchElementException(); throw new ConcurrentModificationException(); } - public void remove() { - if (lastRet < 0) - throw new IllegalStateException(); + public long estimateSize() { + return getFence() - index; + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + + // A tiny bit set implementation + + private static long[] nBits(int n) { + return new long[((n - 1) >> 6) + 1]; + } + private static void setBit(long[] bits, int i) { + bits[i >> 6] |= 1L << i; + } + private static boolean isClear(long[] bits, int i) { + return (bits[i >> 6] & (1L << i)) == 0; + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + @Override + public boolean removeIf(Predicate filter) { + return removeIf(filter, 0, size); + } + + /** + * Removes all elements satisfying the given predicate, from index + * i (inclusive) to index end (exclusive). + */ + boolean removeIf(Predicate filter, int i, final int end) { + Objects.requireNonNull(filter); + int expectedModCount = modCount; + final Object[] es = elementData; + // Optimize for initial run of survivors + for (; i < end && !filter.test(elementAt(es, i)); i++) + ; + // Tolerate predicates that reentrantly access the collection for + // read (but writers still get CME), so traverse once to find + // elements to delete, a second pass to physically expunge. + if (i < end) { + final int beg = i; + final long[] deathRow = nBits(end - beg); + deathRow[0] = 1L; // set bit 0 + for (i = beg + 1; i < end; i++) + if (filter.test(elementAt(es, i))) + setBit(deathRow, i - beg); if (modCount != expectedModCount) throw new ConcurrentModificationException(); - ArrayList.this.remove(lastRet); - if (lastRet < cursor) - cursor--; - lastRet = -1; - expectedModCount = modCount; - } - - public void set(E e) { - if (lastRet < 0) - throw new IllegalStateException(); + modCount++; + int w = beg; + for (i = beg; i < end; i++) + if (isClear(deathRow, i - beg)) + es[w++] = es[i]; + shiftTailOverGap(es, w, end); + // checkInvariants(); + return true; + } else { if (modCount != expectedModCount) throw new ConcurrentModificationException(); - ArrayList.this.set(lastRet, e); - expectedModCount = modCount; - } + // checkInvariants(); + return false; + } + } - public void add(E e) { - if (modCount != expectedModCount) - throw new ConcurrentModificationException(); - ArrayList.this.add(cursor++, e); - lastRet = -1; - expectedModCount = modCount; - } + @Override + public void replaceAll(UnaryOperator operator) { + replaceAllRange(operator, 0, size); + } + + private void replaceAllRange(UnaryOperator operator, int i, int end) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final Object[] es = elementData; + for (; modCount == expectedModCount && i < end; i++) + es[i] = operator.apply(elementAt(es, i)); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + // checkInvariants(); } + @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++; + // checkInvariants(); + } + + void checkInvariants() { + // assert size >= 0; + // assert size == elementData.length || elementData[size] == null; + } }