/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.util.concurrent; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; import libcore.util.EmptyArray; import libcore.util.Objects; /** * A thread-safe random-access list. * * <p>Read operations (including {@link #get}) do not block and may overlap with * update operations. Reads reflect the results of the most recently completed * operations. Aggregate operations like {@link #addAll} and {@link #clear} are * atomic; they never expose an intermediate state. * * <p>Iterators of this list never throw {@link * ConcurrentModificationException}. When an iterator is created, it keeps a * copy of the list's contents. It is always safe to iterate this list, but * iterations may not reflect the latest state of the list. * * <p>Iterators returned by this list and its sub lists cannot modify the * underlying list. In particular, {@link Iterator#remove}, {@link * ListIterator#add} and {@link ListIterator#set} all throw {@link * UnsupportedOperationException}. * * <p>This class offers extended API beyond the {@link List} interface. It * includes additional overloads for indexed search ({@link #indexOf} and {@link * #lastIndexOf}) and methods for conditional adds ({@link #addIfAbsent} and * {@link #addAllAbsent}). */ public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable { private static final long serialVersionUID = 8673264195747942595L; /** * Holds the latest snapshot of the list's data. This field is volatile so * that data can be read without synchronization. As a consequence, all * writes to this field must be atomic; it is an error to modify the * contents of an array after it has been assigned to this field. * * Synchronization is required by all update operations. This defends * against one update clobbering the result of another operation. For * example, 100 threads simultaneously calling add() will grow the list's * size by 100 when they have completed. No update operations are lost! * * Maintainers should be careful to read this field only once in * non-blocking read methods. Write methods must be synchronized to avoid * clobbering concurrent writes. */ private transient volatile Object[] elements; /** * Creates a new empty instance. */ public CopyOnWriteArrayList() { elements = EmptyArray.OBJECT; } /** * Creates a new instance containing the elements of {@code collection}. */ @SuppressWarnings("unchecked") public CopyOnWriteArrayList(Collection<? extends E> collection) { this((E[]) collection.toArray()); } /** * Creates a new instance containing the elements of {@code array}. */ public CopyOnWriteArrayList(E[] array) { this.elements = Arrays.copyOf(array, array.length, Object[].class); } @Override public Object clone() { try { CopyOnWriteArrayList result = (CopyOnWriteArrayList) super.clone(); result.elements = result.elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } public int size() { return elements.length; } @SuppressWarnings("unchecked") public E get(int index) { return (E) elements[index]; } public boolean contains(Object o) { return indexOf(o) != -1; } public boolean containsAll(Collection<?> collection) { Object[] snapshot = elements; return containsAll(collection, snapshot, 0, snapshot.length); } static boolean containsAll(Collection<?> collection, Object[] snapshot, int from, int to) { for (Object o : collection) { if (indexOf(o, snapshot, from, to) == -1) { return false; } } return true; } /** * Searches this list for {@code object} and returns the index of the first * occurrence that is at or after {@code from}. * * @return the index or -1 if the object was not found. */ public int indexOf(E object, int from) { Object[] snapshot = elements; return indexOf(object, snapshot, from, snapshot.length); } public int indexOf(Object object) { Object[] snapshot = elements; return indexOf(object, snapshot, 0, snapshot.length); } /** * Searches this list for {@code object} and returns the index of the last * occurrence that is before {@code to}. * * @return the index or -1 if the object was not found. */ public int lastIndexOf(E object, int to) { Object[] snapshot = elements; return lastIndexOf(object, snapshot, 0, to); } public int lastIndexOf(Object object) { Object[] snapshot = elements; return lastIndexOf(object, snapshot, 0, snapshot.length); } public boolean isEmpty() { return elements.length == 0; } /** * Returns an {@link Iterator} that iterates over the elements of this list * as they were at the time of this method call. Changes to the list made * after this method call will not be reflected by the iterator, nor will * they trigger a {@link ConcurrentModificationException}. * * <p>The returned iterator does not support {@link Iterator#remove()}. */ public Iterator<E> iterator() { Object[] snapshot = elements; return new CowIterator<E>(snapshot, 0, snapshot.length); } /** * Returns a {@link ListIterator} that iterates over the elements of this * list as they were at the time of this method call. Changes to the list * made after this method call will not be reflected by the iterator, nor * will they trigger a {@link ConcurrentModificationException}. * * <p>The returned iterator does not support {@link ListIterator#add}, * {@link ListIterator#set} or {@link Iterator#remove()}, */ public ListIterator<E> listIterator(int index) { Object[] snapshot = elements; if (index < 0 || index > snapshot.length) { throw new IndexOutOfBoundsException("index=" + index + ", length=" + snapshot.length); } CowIterator<E> result = new CowIterator<E>(snapshot, 0, snapshot.length); result.index = index; return result; } /** * Equivalent to {@code listIterator(0)}. */ public ListIterator<E> listIterator() { Object[] snapshot = elements; return new CowIterator<E>(snapshot, 0, snapshot.length); } public List<E> subList(int from, int to) { Object[] snapshot = elements; if (from < 0 || from > to || to > snapshot.length) { throw new IndexOutOfBoundsException("from=" + from + ", to=" + to + ", list size=" + snapshot.length); } return new CowSubList(snapshot, from, to); } public Object[] toArray() { return elements.clone(); } @SuppressWarnings({"unchecked","SuspiciousSystemArraycopy"}) public <T> T[] toArray(T[] contents) { Object[] snapshot = elements; if (snapshot.length > contents.length) { return (T[]) Arrays.copyOf(snapshot, snapshot.length, contents.getClass()); } System.arraycopy(snapshot, 0, contents, 0, snapshot.length); if (snapshot.length < contents.length) { contents[snapshot.length] = null; } return contents; } @Override public boolean equals(Object other) { if (other instanceof CopyOnWriteArrayList) { return this == other || Arrays.equals(elements, ((CopyOnWriteArrayList<?>) other).elements); } else if (other instanceof List) { Object[] snapshot = elements; Iterator<?> i = ((List<?>) other).iterator(); for (Object o : snapshot) { if (!i.hasNext() || !Objects.equal(o, i.next())) { return false; } } return !i.hasNext(); } else { return false; } } @Override public int hashCode() { return Arrays.hashCode(elements); } @Override public String toString() { return Arrays.toString(elements); } public synchronized boolean add(E e) { Object[] newElements = new Object[elements.length + 1]; System.arraycopy(elements, 0, newElements, 0, elements.length); newElements[elements.length] = e; elements = newElements; return true; } public synchronized void add(int index, E e) { Object[] newElements = new Object[elements.length + 1]; System.arraycopy(elements, 0, newElements, 0, index); newElements[index] = e; System.arraycopy(elements, index, newElements, index + 1, elements.length - index); elements = newElements; } public synchronized boolean addAll(Collection<? extends E> collection) { return addAll(elements.length, collection); } public synchronized boolean addAll(int index, Collection<? extends E> collection) { Object[] toAdd = collection.toArray(); Object[] newElements = new Object[elements.length + toAdd.length]; System.arraycopy(elements, 0, newElements, 0, index); System.arraycopy(toAdd, 0, newElements, index, toAdd.length); System.arraycopy(elements, index, newElements, index + toAdd.length, elements.length - index); elements = newElements; return toAdd.length > 0; } /** * Adds the elements of {@code collection} that are not already present in * this list. If {@code collection} includes a repeated value, at most one * occurrence of that value will be added to this list. Elements are added * at the end of this list. * * <p>Callers of this method may prefer {@link CopyOnWriteArraySet}, whose * API is more appropriate for set operations. */ public synchronized int addAllAbsent(Collection<? extends E> collection) { Object[] toAdd = collection.toArray(); Object[] newElements = new Object[elements.length + toAdd.length]; System.arraycopy(elements, 0, newElements, 0, elements.length); int addedCount = 0; for (Object o : toAdd) { if (indexOf(o, newElements, 0, elements.length + addedCount) == -1) { newElements[elements.length + addedCount++] = o; } } if (addedCount < toAdd.length) { newElements = Arrays.copyOfRange( newElements, 0, elements.length + addedCount); // trim to size } elements = newElements; return addedCount; } /** * Adds {@code object} to the end of this list if it is not already present. * * <p>Callers of this method may prefer {@link CopyOnWriteArraySet}, whose * API is more appropriate for set operations. */ public synchronized boolean addIfAbsent(E object) { if (contains(object)) { return false; } add(object); return true; } @Override public synchronized void clear() { elements = EmptyArray.OBJECT; } public synchronized E remove(int index) { @SuppressWarnings("unchecked") E removed = (E) elements[index]; removeRange(index, index + 1); return removed; } public synchronized boolean remove(Object o) { int index = indexOf(o); if (index == -1) { return false; } remove(index); return true; } public synchronized boolean removeAll(Collection<?> collection) { return removeOrRetain(collection, false, 0, elements.length) != 0; } public synchronized boolean retainAll(Collection<?> collection) { return removeOrRetain(collection, true, 0, elements.length) != 0; } /** * Removes or retains the elements in {@code collection}. Returns the number * of elements removed. */ private int removeOrRetain(Collection<?> collection, boolean retain, int from, int to) { for (int i = from; i < to; i++) { if (collection.contains(elements[i]) == retain) { continue; } /* * We've encountered an element that must be removed! Create a new * array and copy in the surviving elements one by one. */ Object[] newElements = new Object[elements.length - 1]; System.arraycopy(elements, 0, newElements, 0, i); int newSize = i; for (int j = i + 1; j < to; j++) { if (collection.contains(elements[j]) == retain) { newElements[newSize++] = elements[j]; } } /* * Copy the elements after 'to'. This is only useful for sub lists, * where 'to' will be less than elements.length. */ System.arraycopy(elements, to, newElements, newSize, elements.length - to); newSize += (elements.length - to); if (newSize < newElements.length) { newElements = Arrays.copyOfRange(newElements, 0, newSize); // trim to size } int removed = elements.length - newElements.length; elements = newElements; return removed; } // we made it all the way through the loop without making any changes return 0; } public synchronized E set(int index, E e) { Object[] newElements = elements.clone(); @SuppressWarnings("unchecked") E result = (E) newElements[index]; newElements[index] = e; elements = newElements; return result; } private void removeRange(int from, int to) { Object[] newElements = new Object[elements.length - (to - from)]; System.arraycopy(elements, 0, newElements, 0, from); System.arraycopy(elements, to, newElements, from, elements.length - to); elements = newElements; } static int lastIndexOf(Object o, Object[] data, int from, int to) { if (o == null) { for (int i = to - 1; i >= from; i--) { if (data[i] == null) { return i; } } } else { for (int i = to - 1; i >= from; i--) { if (o.equals(data[i])) { return i; } } } return -1; } static int indexOf(Object o, Object[] data, int from, int to) { if (o == null) { for (int i = from; i < to; i++) { if (data[i] == null) { return i; } } } else { for (int i = from; i < to; i++) { if (o.equals(data[i])) { return i; } } } return -1; } final Object[] getArray() { // CopyOnWriteArraySet needs this. return elements; } /** * The sub list is thread safe and supports non-blocking reads. Doing so is * more difficult than in the full list, because each read needs to examine * four fields worth of state: * - the elements array of the full list * - two integers for the bounds of this sub list * - the expected elements array (to detect concurrent modification) * * This is accomplished by aggregating the sub list's three fields into a * single snapshot object representing the current slice. This permits reads * to be internally consistent without synchronization. This takes advantage * of Java's concurrency semantics for final fields. */ class CowSubList extends AbstractList<E> { /* * An immutable snapshot of a sub list's state. By gathering all three * of the sub list's fields in an immutable object, */ private volatile Slice slice; public CowSubList(Object[] expectedElements, int from, int to) { this.slice = new Slice(expectedElements, from, to); } @Override public int size() { Slice slice = this.slice; return slice.to - slice.from; } @Override public boolean isEmpty() { Slice slice = this.slice; return slice.from == slice.to; } @SuppressWarnings("unchecked") @Override public E get(int index) { Slice slice = this.slice; Object[] snapshot = elements; slice.checkElementIndex(index); slice.checkConcurrentModification(snapshot); return (E) snapshot[index + slice.from]; } @Override public Iterator<E> iterator() { return listIterator(0); } @Override public ListIterator<E> listIterator() { return listIterator(0); } @Override public ListIterator<E> listIterator(int index) { Slice slice = this.slice; Object[] snapshot = elements; slice.checkPositionIndex(index); slice.checkConcurrentModification(snapshot); CowIterator<E> result = new CowIterator<E>(snapshot, slice.from, slice.to); result.index = slice.from + index; return result; } @Override public int indexOf(Object object) { Slice slice = this.slice; Object[] snapshot = elements; slice.checkConcurrentModification(snapshot); int result = CopyOnWriteArrayList.indexOf(object, snapshot, slice.from, slice.to); return (result != -1) ? (result - slice.from) : -1; } @Override public int lastIndexOf(Object object) { Slice slice = this.slice; Object[] snapshot = elements; slice.checkConcurrentModification(snapshot); int result = CopyOnWriteArrayList.lastIndexOf(object, snapshot, slice.from, slice.to); return (result != -1) ? (result - slice.from) : -1; } @Override public boolean contains(Object object) { return indexOf(object) != -1; } @Override public boolean containsAll(Collection<?> collection) { Slice slice = this.slice; Object[] snapshot = elements; slice.checkConcurrentModification(snapshot); return CopyOnWriteArrayList.containsAll(collection, snapshot, slice.from, slice.to); } @Override public List<E> subList(int from, int to) { Slice slice = this.slice; if (from < 0 || from > to || to > size()) { throw new IndexOutOfBoundsException("from=" + from + ", to=" + to + ", list size=" + size()); } return new CowSubList(slice.expectedElements, slice.from + from, slice.from + to); } @Override public E remove(int index) { synchronized (CopyOnWriteArrayList.this) { slice.checkElementIndex(index); slice.checkConcurrentModification(elements); E removed = CopyOnWriteArrayList.this.remove(slice.from + index); slice = new Slice(elements, slice.from, slice.to - 1); return removed; } } @Override public void clear() { synchronized (CopyOnWriteArrayList.this) { slice.checkConcurrentModification(elements); CopyOnWriteArrayList.this.removeRange(slice.from, slice.to); slice = new Slice(elements, slice.from, slice.from); } } @Override public void add(int index, E object) { synchronized (CopyOnWriteArrayList.this) { slice.checkPositionIndex(index); slice.checkConcurrentModification(elements); CopyOnWriteArrayList.this.add(index + slice.from, object); slice = new Slice(elements, slice.from, slice.to + 1); } } @Override public boolean add(E object) { synchronized (CopyOnWriteArrayList.this) { add(slice.to - slice.from, object); return true; } } @Override public boolean addAll(int index, Collection<? extends E> collection) { synchronized (CopyOnWriteArrayList.this) { slice.checkPositionIndex(index); slice.checkConcurrentModification(elements); int oldSize = elements.length; boolean result = CopyOnWriteArrayList.this.addAll(index + slice.from, collection); slice = new Slice(elements, slice.from, slice.to + (elements.length - oldSize)); return result; } } @Override public boolean addAll(Collection<? extends E> collection) { synchronized (CopyOnWriteArrayList.this) { return addAll(size(), collection); } } @Override public E set(int index, E object) { synchronized (CopyOnWriteArrayList.this) { slice.checkElementIndex(index); slice.checkConcurrentModification(elements); E result = CopyOnWriteArrayList.this.set(index + slice.from, object); slice = new Slice(elements, slice.from, slice.to); return result; } } @Override public boolean remove(Object object) { synchronized (CopyOnWriteArrayList.this) { int index = indexOf(object); if (index == -1) { return false; } remove(index); return true; } } @Override public boolean removeAll(Collection<?> collection) { synchronized (CopyOnWriteArrayList.this) { slice.checkConcurrentModification(elements); int removed = removeOrRetain(collection, false, slice.from, slice.to); slice = new Slice(elements, slice.from, slice.to - removed); return removed != 0; } } @Override public boolean retainAll(Collection<?> collection) { synchronized (CopyOnWriteArrayList.this) { slice.checkConcurrentModification(elements); int removed = removeOrRetain(collection, true, slice.from, slice.to); slice = new Slice(elements, slice.from, slice.to - removed); return removed != 0; } } } static class Slice { private final Object[] expectedElements; private final int from; private final int to; Slice(Object[] expectedElements, int from, int to) { this.expectedElements = expectedElements; this.from = from; this.to = to; } /** * Throws if {@code index} doesn't identify an element in the array. */ void checkElementIndex(int index) { if (index < 0 || index >= to - from) { throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from)); } } /** * Throws if {@code index} doesn't identify an insertion point in the * array. Unlike element index, it's okay to add or iterate at size(). */ void checkPositionIndex(int index) { if (index < 0 || index > to - from) { throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from)); } } void checkConcurrentModification(Object[] snapshot) { if (expectedElements != snapshot) { throw new ConcurrentModificationException(); } } } /** * Iterates an immutable snapshot of the list. */ static class CowIterator<E> implements ListIterator<E> { private final Object[] snapshot; private final int from; private final int to; private int index = 0; CowIterator(Object[] snapshot, int from, int to) { this.snapshot = snapshot; this.from = from; this.to = to; this.index = from; } public void add(E object) { throw new UnsupportedOperationException(); } public boolean hasNext() { return index < to; } public boolean hasPrevious() { return index > from; } @SuppressWarnings("unchecked") public E next() { if (index < to) { return (E) snapshot[index++]; } else { throw new NoSuchElementException(); } } public int nextIndex() { return index; } @SuppressWarnings("unchecked") public E previous() { if (index > from) { return (E) snapshot[--index]; } else { throw new NoSuchElementException(); } } public int previousIndex() { return index - 1; } public void remove() { throw new UnsupportedOperationException(); } public void set(E object) { throw new UnsupportedOperationException(); } } private void writeObject(ObjectOutputStream out) throws IOException { Object[] snapshot = elements; out.defaultWriteObject(); out.writeInt(snapshot.length); for (Object o : snapshot) { out.writeObject(o); } } private synchronized void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Object[] snapshot = new Object[in.readInt()]; for (int i = 0; i < snapshot.length; i++) { snapshot[i] = in.readObject(); } elements = snapshot; } }