/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group. Adapted and released, under explicit permission, * from JDK ArrayList.java which carries the following copyright: * * Copyright 1997 by Sun Microsystems, Inc., * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Sun Microsystems, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Sun. */ package java.util.concurrent; import java.util.*; /** * A thread-safe variant of {@link java.util.ArrayList} in which all mutative * operations (add, set, and so on) are implemented by * making a fresh copy of the underlying array. * *

This is ordinarily too costly, but may be more efficient * than alternatives when traversal operations vastly outnumber * mutations, and is useful when you cannot or don't want to * synchronize traversals, yet need to preclude interference among * concurrent threads. The "snapshot" style iterator method uses a * reference to the state of the array at the point that the iterator * was created. This array never changes during the lifetime of the * iterator, so interference is impossible and the iterator is * guaranteed not to throw ConcurrentModificationException. * The iterator will not reflect additions, removals, or changes to * the list since the iterator was created. Element-changing * operations on iterators themselves (remove, set, and * add) are not supported. These methods throw * UnsupportedOperationException. * *

All elements are permitted, including null. * *

This class is a member of the * * Java Collections Framework. * * @since 1.5 * @author Doug Lea * @param the type of elements held in this collection */ public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; /** * The held array. Directly accessed only within synchronized * methods. */ private volatile transient E[] array; /** * Accessor to the array intended to be called from * within unsynchronized read-only methods */ private E[] array() { return array; } /** * Creates an empty list. */ public CopyOnWriteArrayList() { array = (E[]) new Object[0]; } /** * Creates a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection of initially held elements * @throws NullPointerException if the specified collection is null */ public CopyOnWriteArrayList(Collection c) { array = (E[]) new Object[c.size()]; Iterator i = c.iterator(); int size = 0; while (i.hasNext()) array[size++] = i.next(); } /** * Creates a list holding a copy of the given array. * * @param toCopyIn the array (a copy of this array is used as the * internal array) */ public CopyOnWriteArrayList(E[] toCopyIn) { copyIn(toCopyIn, 0, toCopyIn.length); } /** * Replaces the held array with a copy of the n elements * of the provided array, starting at position first. To * copy an entire array, call with arguments (array, 0, * array.length). * @param toCopyIn the array. A copy of the indicated elements of * this array is used as the internal array. * @param first The index of first position of the array to * start copying from. * @param n the number of elements to copy. This will be the new size of * the list. */ private synchronized void copyIn(E[] toCopyIn, int first, int n) { array = (E[]) new Object[n]; System.arraycopy(toCopyIn, first, array, 0, n); } /** * Returns the number of elements in this list. * * @return the number of elements in this list */ public int size() { return array().length; } /** * Returns true if this list contains no elements. * * @return 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)). * * @param o element whose presence in this list is to be tested * @return true if this list contains the specified element */ public boolean contains(Object o) { E[] elementData = array(); int len = elementData.length; return indexOf(o, elementData, len) >= 0; } /** * {@inheritDoc} */ public int indexOf(Object o) { E[] elementData = array(); int len = elementData.length; return indexOf(o, elementData, len); } /** * static version allows repeated call without needing * to grab lock for array each time */ private static int indexOf(Object o, Object[] elementData, int len) { if (o == null) { for (int i = 0; i < len; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < len; i++) if (o.equals(elementData[i])) return i; } return -1; } /** * Returns the index of the first occurrence of the specified element in * this list, searching forwards from index, or returns -1 if * the element is not found. * More formally, returns the lowest index i such that * (i >= index && (e==null ? get(i)==null : e.equals(get(i)))), * or -1 if there is no such index. * * @param e element to search for * @param index index to start searching from * @return the index of the first occurrence of the element in * this list at position index or later in the list; * -1 if the element is not found. * @throws IndexOutOfBoundsException if the specified index is negative */ public int indexOf(E e, int index) { E[] elementData = array(); int elementCount = elementData.length; if (e == null) { for (int i = index ; i < elementCount ; i++) if (elementData[i]==null) return i; } else { for (int i = index ; i < elementCount ; i++) if (e.equals(elementData[i])) return i; } return -1; } /** * {@inheritDoc} */ public int lastIndexOf(Object o) { E[] elementData = array(); int len = elementData.length; return lastIndexOf(o, elementData, len); } private static int lastIndexOf(Object o, Object[] elementData, int len) { if (o == null) { for (int i = len-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = len-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } /** * Returns the index of the last occurrence of the specified element in * this list, searching backwards from index, or returns -1 if * the element is not found. * More formally, returns the highest index i such that * (i <= index && (e==null ? get(i)==null : e.equals(get(i)))), * or -1 if there is no such index. * * @param e element to search for * @param index index to start searching backwards from * @return the index of the last occurrence of the element at position * less than or equal to index in this list; * -1 if the element is not found. * @throws IndexOutOfBoundsException if the specified index is greater * than or equal to the current size of this list */ public int lastIndexOf(E e, int index) { // needed in order to compile on 1.2b3 E[] elementData = array(); if (e == null) { for (int i = index; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = index; i >= 0; i--) if (e.equals(elementData[i])) return i; } return -1; } /** * Returns a shallow copy of this list. (The elements themselves * are not copied.) * * @return a clone of this list */ public Object clone() { try { E[] elementData = array(); CopyOnWriteArrayList v = (CopyOnWriteArrayList)super.clone(); v.array = (E[]) new Object[elementData.length]; System.arraycopy(elementData, 0, v.array, 0, elementData.length); return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } /** * Returns an array containing all of the elements in this list * in proper sequence (from first to last element). * *

The returned array will be "safe" in that no references to it are * maintained by this list. (In other words, this method must allocate * a new array). The caller is thus free to modify the returned array. * *

This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all the elements in this list */ public Object[] toArray() { Object[] elementData = array(); Object[] result = new Object[elementData.length]; System.arraycopy(elementData, 0, result, 0, elementData.length); return result; } /** * Returns an array containing all of the elements in this list in * proper sequence (from first to last element); the runtime type of * the returned array is that of the specified array. If the list fits * in the specified array, it is returned therein. Otherwise, a new * array is allocated with the runtime type of the specified array and * the size of this list. * *

If this list fits in the specified array with room to spare * (i.e., the array has more elements than this list), the element in * the array immediately following the end of the list is set to * null. (This is useful in determining the length of this * list only if the caller knows that this list does not contain * any null elements.) * *

Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * *

Suppose x is a list known to contain only strings. * The following code can be used to dump the list into a newly * allocated array of String: * *

     *     String[] y = x.toArray(new String[0]);
* * Note that toArray(new Object[0]) is identical in function to * toArray(). * * @param a the array into which the elements of the list are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing all the elements in this list * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this list * @throws NullPointerException if the specified array is null */ public T[] toArray(T a[]) { E[] elementData = array(); if (a.length < elementData.length) a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), elementData.length); System.arraycopy(elementData, 0, a, 0, elementData.length); if (a.length > elementData.length) a[elementData.length] = null; return a; } // Positional Access Operations /** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { E[] elementData = array(); rangeCheck(index, elementData.length); return elementData[index]; } /** * Replaces the element at the specified position in this list with the * specified element. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public synchronized E set(int index, E element) { int len = array.length; rangeCheck(index, len); E oldValue = array[index]; boolean same = (oldValue == element || (element != null && element.equals(oldValue))); if (!same) { E[] newArray = (E[]) new Object[len]; System.arraycopy(array, 0, newArray, 0, len); newArray[index] = element; array = newArray; } return oldValue; } /** * Appends the specified element to the end of this list. * * @param element element to be appended to this list * @return true (as per the spec for {@link Collection#add}) */ public synchronized boolean add(E element) { int len = array.length; E[] newArray = (E[]) new Object[len+1]; System.arraycopy(array, 0, newArray, 0, len); newArray[len] = element; array = newArray; return true; } /** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @throws IndexOutOfBoundsException {@inheritDoc} */ public synchronized void add(int index, E element) { int len = array.length; if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len); E[] newArray = (E[]) new Object[len+1]; System.arraycopy(array, 0, newArray, 0, index); newArray[index] = element; System.arraycopy(array, index, newArray, index+1, len - index); array = newArray; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). Returns the element that was removed from the list. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public synchronized E remove(int index) { int len = array.length; rangeCheck(index, len); E oldValue = array[index]; E[] newArray = (E[]) new Object[len-1]; System.arraycopy(array, 0, newArray, 0, index); int numMoved = len - index - 1; if (numMoved > 0) System.arraycopy(array, index+1, newArray, index, numMoved); array = newArray; return oldValue; } /** * Removes the first occurrence of the specified element from this list, * if it is present. If this 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 * 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 */ public synchronized boolean remove(Object o) { int len = array.length; if (len == 0) return false; // Copy while searching for element to remove // This wins in the normal case of element being present int newlen = len-1; E[] newArray = (E[]) new Object[newlen]; for (int i = 0; i < newlen; ++i) { if (o == array[i] || (o != null && o.equals(array[i]))) { // found one; copy remaining and exit for (int k = i + 1; k < len; ++k) newArray[k-1] = array[k]; array = newArray; return true; } else newArray[i] = array[i]; } // special handling for last cell if (o == array[newlen] || (o != null && o.equals(array[newlen]))) { array = newArray; return true; } else return false; // throw away copy } /** * Removes from this list all of the elements whose index is between * fromIndex, inclusive, and 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.) * * @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) */ private synchronized void removeRange(int fromIndex, int toIndex) { int len = array.length; if (fromIndex < 0 || fromIndex >= len || toIndex > len || toIndex < fromIndex) throw new IndexOutOfBoundsException(); int numMoved = len - toIndex; int newlen = len - (toIndex-fromIndex); E[] newArray = (E[]) new Object[newlen]; System.arraycopy(array, 0, newArray, 0, fromIndex); System.arraycopy(array, toIndex, newArray, fromIndex, numMoved); array = newArray; } /** * Append the element if not present. * @param element element to be added to this list, if absent * @return true if added */ public synchronized boolean addIfAbsent(E element) { // Copy while checking if already present. // This wins in the most common case where it is not present int len = array.length; E[] newArray = (E[]) new Object[len + 1]; for (int i = 0; i < len; ++i) { if (element == array[i] || (element != null && element.equals(array[i]))) return false; // exit, throwing away copy else newArray[i] = array[i]; } newArray[len] = element; array = newArray; return true; } /** * Returns true if this list contains all of the elements of the * specified collection. * * @param c collection to be checked for containment in this list * @return true if this list contains all of the elements of the * specified collection * @throws NullPointerException if the specified collection is null */ public boolean containsAll(Collection c) { E[] elementData = array(); int len = elementData.length; Iterator e = c.iterator(); while (e.hasNext()) if (indexOf(e.next(), elementData, len) < 0) return false; return true; } /** * Removes from this list all of its elements that are contained in * the specified collection. This is a particularly expensive operation * in this class because of the need for an internal temporary array. * * @param c collection containing elements to be removed from this list * @return true if this list changed as a result of the call * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public synchronized boolean removeAll(Collection c) { E[] elementData = array; int len = elementData.length; if (len == 0) return false; // temp array holds those elements we know we want to keep E[] temp = (E[]) new Object[len]; int newlen = 0; for (int i = 0; i < len; ++i) { E element = elementData[i]; if (!c.contains(element)) { temp[newlen++] = element; } } if (newlen == len) return false; // copy temp as new array E[] newArray = (E[]) new Object[newlen]; System.arraycopy(temp, 0, newArray, 0, newlen); array = newArray; return true; } /** * 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. * * @param c collection containing elements to be retained in this list * @return true if this list changed as a result of the call * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public synchronized boolean retainAll(Collection c) { E[] elementData = array; int len = elementData.length; if (len == 0) return false; E[] temp = (E[]) new Object[len]; int newlen = 0; for (int i = 0; i < len; ++i) { E element = elementData[i]; if (c.contains(element)) { temp[newlen++] = element; } } if (newlen == len) return false; E[] newArray = (E[]) new Object[newlen]; System.arraycopy(temp, 0, newArray, 0, newlen); array = newArray; return true; } /** * Appends all of the elements in the specified collection that * are not already contained in this list, to the end of * this list, in the order that they are returned by the * specified collection's iterator. * * @param c collection containing elements to be added to this list * @return the number of elements added * @throws NullPointerException if the specified collection is null */ public synchronized int addAllAbsent(Collection c) { int numNew = c.size(); if (numNew == 0) return 0; E[] elementData = array; int len = elementData.length; E[] temp = (E[]) new Object[numNew]; int added = 0; Iterator e = c.iterator(); while (e.hasNext()) { E element = e.next(); if (indexOf(element, elementData, len) < 0) { if (indexOf(element, temp, added) < 0) { temp[added++] = element; } } } if (added == 0) return 0; E[] newArray = (E[]) new Object[len+added]; System.arraycopy(elementData, 0, newArray, 0, len); System.arraycopy(temp, 0, newArray, len, added); array = newArray; return added; } /** * Removes all of the elements from this list. * */ public synchronized void clear() { array = (E[]) new Object[0]; } /** * Appends all of the elements in the specified collection to the end * of this list, in the order that they are returned by the specified * collection's iterator. * * @param c collection containing elements to be added to this list * @return true if any elements are added * @throws NullPointerException if the specified collection is null */ public synchronized boolean addAll(Collection c) { int numNew = c.size(); if (numNew == 0) return false; int len = array.length; E[] newArray = (E[]) new Object[len+numNew]; System.arraycopy(array, 0, newArray, 0, len); Iterator e = c.iterator(); for (int i=0; itrue if this list changed as a result of the call * @throws IndexOutOfBoundsException {@inheritDoc} * @throws NullPointerException if the specified collection is null */ public synchronized boolean addAll(int index, Collection c) { int len = array.length; if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len); int numNew = c.size(); if (numNew == 0) return false; E[] newArray = (E[]) new Object[len+numNew]; System.arraycopy(array, 0, newArray, 0, len); int numMoved = len - index; if (numMoved > 0) System.arraycopy(array, index, newArray, index + numNew, numMoved); Iterator e = c.iterator(); for (int i=0; i= length || index < 0) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+ length); } /** * Save the state of the list to a stream (i.e., serialize it). * * @serialData The length of the array backing the list is emitted * (int), followed by all of its elements (each an Object) * in the proper order. * @param s the stream */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff s.defaultWriteObject(); E[] elementData = array(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; i