| /* CopyOnWriteArrayList.java | |
| Copyright (C) 2006 Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.util.concurrent; | |
| import java.io.IOException; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.Serializable; | |
| import java.lang.reflect.Array; | |
| 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; | |
| /** | |
| * A thread-safe implementation of an ArrayList. A CopyOnWriteArrayList is | |
| * as special ArrayList which performs copies of the underlying storage | |
| * each time a write (<code>remove</code>, <code>add</code> etc..) operation | |
| * is performed.<br /> | |
| * <br /> | |
| * The update operation in this class run usually in <code>O(n)</code> or worse, | |
| * but traversal operations are fast and efficient, especially when running in | |
| * a multi-thread environment without the need to design complex synchronize | |
| * mechanisms.<br /> | |
| * <br /> | |
| * <code>Iterator</code>s in this class work on a snapshot of the backing store | |
| * at the moment the iterator itself was created, hence the iterator will not | |
| * reflect changes in the underlying storage. Thus, update operation on the | |
| * <code>Iterator</code>s are not supported, but as interferences from other | |
| * threads are impossible, no <code>ConcurrentModificationException</code> | |
| * will be ever thrown from within the <code>Iterator</code>. | |
| * <br /><br /> | |
| * This class is especially useful when used with event handling, like the | |
| * following code demonstrates:<br /> | |
| * <code><pre> | |
| * | |
| * CopyOnWriteArrayList<EventListener> listeners = | |
| * new CopyOnWriteArrayList<EventListener>(); | |
| * | |
| * [...] | |
| * | |
| * for (final EventListener listener : listeners) | |
| * { | |
| * Runnable dispatcher = new Runnable() { | |
| * public void run() | |
| * { | |
| * listener.preferenceChange(event); | |
| * } | |
| * }; | |
| * | |
| * Executor executor = Executors.newSingleThreadExecutor(); | |
| * executor.execute(dispatcher); | |
| * } | |
| * </pre></code> | |
| * | |
| * @since 1.5 | |
| */ | |
| public class CopyOnWriteArrayList<E> | |
| implements List<E>, RandomAccess, Cloneable, Serializable | |
| { | |
| /** | |
| * | |
| */ | |
| private static final long serialVersionUID = 8673264195747942595L; | |
| /** | |
| * Where the data is stored. | |
| */ | |
| private transient E[] data; | |
| /** | |
| * Construct a new ArrayList with the default capacity (16). | |
| */ | |
| public CopyOnWriteArrayList() | |
| { | |
| data = (E[]) new Object[0]; | |
| } | |
| /** | |
| * Construct a new ArrayList, and initialize it with the elements in the | |
| * supplied Collection. The initial capacity is 110% of the Collection's size. | |
| * | |
| * @param c | |
| * the collection whose elements will initialize this list | |
| * @throws NullPointerException | |
| * if c is null | |
| */ | |
| public CopyOnWriteArrayList(Collection< ? extends E> c) | |
| { | |
| // FIXME ... correct? use c.toArray() | |
| data = (E[]) new Object[c.size()]; | |
| int index = 0; | |
| for (E value : c) | |
| data[index++] = value; | |
| } | |
| /** | |
| * Construct a new ArrayList, and initialize it with the elements in the | |
| * supplied array. | |
| * | |
| * @param array | |
| * the array used to initialize this list | |
| * @throws NullPointerException | |
| * if array is null | |
| */ | |
| public CopyOnWriteArrayList(E[] array) | |
| { | |
| data = (E[]) array.clone(); | |
| } | |
| /** | |
| * Returns the number of elements in this list. | |
| * | |
| * @return the list size | |
| */ | |
| public int size() | |
| { | |
| return data.length; | |
| } | |
| /** | |
| * Checks if the list is empty. | |
| * | |
| * @return true if there are no elements | |
| */ | |
| public boolean isEmpty() | |
| { | |
| return data.length == 0; | |
| } | |
| /** | |
| * Returns true if element is in this ArrayList. | |
| * | |
| * @param e | |
| * the element whose inclusion in the List is being tested | |
| * @return true if the list contains e | |
| */ | |
| public boolean contains(Object e) | |
| { | |
| return indexOf(e) != -1; | |
| } | |
| /** | |
| * Tests whether this collection contains all the elements in a given | |
| * collection. This implementation iterates over the given collection, | |
| * testing whether each element is contained in this collection. If any one | |
| * is not, false is returned. Otherwise true is returned. | |
| * | |
| * @param c the collection to test against | |
| * @return true if this collection contains all the elements in the given | |
| * collection | |
| * @throws NullPointerException if the given collection is null | |
| * @see #contains(Object) | |
| */ | |
| public boolean containsAll(Collection<?> c) | |
| { | |
| Iterator<?> itr = c.iterator(); | |
| int pos = c.size(); | |
| while (--pos >= 0) | |
| if (!contains(itr.next())) | |
| return false; | |
| return true; | |
| } | |
| /** | |
| * Returns the lowest index at which element appears in this List, or -1 if it | |
| * does not appear. | |
| * | |
| * @param e | |
| * the element whose inclusion in the List is being tested | |
| * @return the index where e was found | |
| */ | |
| public int indexOf(Object e) | |
| { | |
| E[] data = this.data; | |
| for (int i = 0; i < data.length; i++) | |
| if (equals(e, data[i])) | |
| return i; | |
| return -1; | |
| } | |
| /** | |
| * Return the lowest index greater equal <code>index</code> at which | |
| * <code>e</code> appears in this List, or -1 if it does not | |
| * appear. | |
| * | |
| * @param e the element whose inclusion in the list is being tested | |
| * @param index the index at which the search begins | |
| * @return the index where <code>e</code> was found | |
| */ | |
| public int indexOf(E e, int index) | |
| { | |
| E[] data = this.data; | |
| for (int i = index; i < data.length; i++) | |
| if (equals(e, data[i])) | |
| return i; | |
| return -1; | |
| } | |
| /** | |
| * Returns the highest index at which element appears in this List, or -1 if | |
| * it does not appear. | |
| * | |
| * @param e | |
| * the element whose inclusion in the List is being tested | |
| * @return the index where e was found | |
| */ | |
| public int lastIndexOf(Object e) | |
| { | |
| E[] data = this.data; | |
| for (int i = data.length - 1; i >= 0; i--) | |
| if (equals(e, data[i])) | |
| return i; | |
| return -1; | |
| } | |
| /** | |
| * Returns the highest index lesser equal <code>index</code> at | |
| * which <code>e</code> appears in this List, or -1 if it does not | |
| * appear. | |
| * | |
| * @param e the element whose inclusion in the list is being tested | |
| * @param index the index at which the search begins | |
| * @return the index where <code>e</code> was found | |
| */ | |
| public int lastIndexOf(E e, int index) | |
| { | |
| E[] data = this.data; | |
| for (int i = index; i >= 0; i--) | |
| if (equals(e, data[i])) | |
| return i; | |
| return -1; | |
| } | |
| /** | |
| * Creates a shallow copy of this ArrayList (elements are not cloned). | |
| * | |
| * @return the cloned object | |
| */ | |
| public Object clone() | |
| { | |
| CopyOnWriteArrayList<E> clone = null; | |
| try | |
| { | |
| clone = (CopyOnWriteArrayList<E>) super.clone(); | |
| } | |
| catch (CloneNotSupportedException e) | |
| { | |
| // Impossible to get here. | |
| } | |
| return clone; | |
| } | |
| /** | |
| * Returns an Object array containing all of the elements in this ArrayList. | |
| * The array is independent of this list. | |
| * | |
| * @return an array representation of this list | |
| */ | |
| public Object[] toArray() | |
| { | |
| E[] data = this.data; | |
| E[] array = (E[]) new Object[data.length]; | |
| System.arraycopy(data, 0, array, 0, data.length); | |
| return array; | |
| } | |
| /** | |
| * Returns an Array whose component type is the runtime component type of the | |
| * passed-in Array. The returned Array is populated with all of the elements | |
| * in this ArrayList. If the passed-in Array is not large enough to store all | |
| * of the elements in this List, a new Array will be created and returned; if | |
| * the passed-in Array is <i>larger</i> than the size of this List, then | |
| * size() index will be set to null. | |
| * | |
| * @param a | |
| * the passed-in Array | |
| * @return an array representation of this list | |
| * @throws ArrayStoreException | |
| * if the runtime type of a does not allow an element in this list | |
| * @throws NullPointerException | |
| * if a is null | |
| */ | |
| public <T> T[] toArray(T[] a) | |
| { | |
| E[] data = this.data; | |
| if (a.length < data.length) | |
| a = (T[]) Array.newInstance(a.getClass().getComponentType(), data.length); | |
| else if (a.length > data.length) | |
| a[data.length] = null; | |
| System.arraycopy(data, 0, a, 0, data.length); | |
| return a; | |
| } | |
| /** | |
| * Retrieves the element at the user-supplied index. | |
| * | |
| * @param index | |
| * the index of the element we are fetching | |
| * @throws IndexOutOfBoundsException | |
| * if index < 0 || index >= size() | |
| */ | |
| public E get(int index) | |
| { | |
| return data[index]; | |
| } | |
| /** | |
| * Sets the element at the specified index. The new element, e, can be an | |
| * object of any type or null. | |
| * | |
| * @param index | |
| * the index at which the element is being set | |
| * @param e | |
| * the element to be set | |
| * @return the element previously at the specified index | |
| * @throws IndexOutOfBoundsException | |
| * if index < 0 || index >= 0 | |
| */ | |
| public synchronized E set(int index, E e) | |
| { | |
| E result = data[index]; | |
| E[] newData = (E[]) data.clone(); | |
| newData[index] = e; | |
| data = newData; | |
| return result; | |
| } | |
| /** | |
| * Appends the supplied element to the end of this list. The element, e, can | |
| * be an object of any type or null. | |
| * | |
| * @param e | |
| * the element to be appended to this list | |
| * @return true, the add will always succeed | |
| */ | |
| public synchronized boolean add(E e) | |
| { | |
| E[] data = this.data; | |
| E[] newData = (E[]) new Object[data.length + 1]; | |
| System.arraycopy(data, 0, newData, 0, data.length); | |
| newData[data.length] = e; | |
| this.data = newData; | |
| return true; | |
| } | |
| /** | |
| * Adds the supplied element at the specified index, shifting all elements | |
| * currently at that index or higher one to the right. The element, e, can be | |
| * an object of any type or null. | |
| * | |
| * @param index | |
| * the index at which the element is being added | |
| * @param e | |
| * the item being added | |
| * @throws IndexOutOfBoundsException | |
| * if index < 0 || index > size() | |
| */ | |
| public synchronized void add(int index, E e) | |
| { | |
| E[] data = this.data; | |
| E[] newData = (E[]) new Object[data.length + 1]; | |
| System.arraycopy(data, 0, newData, 0, index); | |
| newData[index] = e; | |
| System.arraycopy(data, index, newData, index + 1, data.length - index); | |
| this.data = newData; | |
| } | |
| /** | |
| * Removes the element at the user-supplied index. | |
| * | |
| * @param index | |
| * the index of the element to be removed | |
| * @return the removed Object | |
| * @throws IndexOutOfBoundsException | |
| * if index < 0 || index >= size() | |
| */ | |
| public synchronized E remove(int index) | |
| { | |
| if (index < 0 || index >= this.size()) | |
| throw new IndexOutOfBoundsException("index = " + index); | |
| E[] snapshot = this.data; | |
| E[] newData = (E[]) new Object[snapshot.length - 1]; | |
| E result = snapshot[index]; | |
| if (index > 0) | |
| System.arraycopy(snapshot, 0, newData, 0, index); | |
| System.arraycopy(snapshot, index + 1, newData, index, | |
| snapshot.length - index - 1); | |
| this.data = newData; | |
| return result; | |
| } | |
| /** | |
| * Remove the first occurrence, if any, of the given object from this list, | |
| * returning <code>true</code> if the object was removed, <code>false</code> | |
| * otherwise. | |
| * | |
| * @param element the object to be removed. | |
| * @return true if element was removed, false otherwise. false means also that | |
| * the underlying storage was unchanged after this operation concluded. | |
| */ | |
| public synchronized boolean remove(Object element) | |
| { | |
| E[] snapshot = this.data; | |
| int len = snapshot.length; | |
| if (len == 0) | |
| return false; | |
| E[] newData = (E[]) new Object[len - 1]; | |
| // search the element to remove while filling the backup array | |
| // this way we can run this method in O(n) | |
| int elementIndex = -1; | |
| for (int i = 0; i < snapshot.length; i++) | |
| { | |
| if (equals(element, snapshot[i])) | |
| { | |
| elementIndex = i; | |
| break; | |
| } | |
| if (i < newData.length) | |
| newData[i] = snapshot[i]; | |
| } | |
| if (elementIndex < 0) | |
| return false; | |
| System.arraycopy(snapshot, elementIndex + 1, newData, elementIndex, | |
| snapshot.length - elementIndex - 1); | |
| this.data = newData; | |
| return true; | |
| } | |
| /** | |
| * Removes all the elements contained in the given collection. | |
| * This method removes the elements that are contained in both | |
| * this list and in the given collection. | |
| * | |
| * @param c the collection containing the elements to be removed from this | |
| * list. | |
| * @return true if at least one element was removed, indicating that | |
| * the list internal storage changed as a result, false otherwise. | |
| */ | |
| public synchronized boolean removeAll(Collection<?> c) | |
| { | |
| if (c.size() == 0) | |
| return false; | |
| E [] snapshot = this.data; | |
| E [] storage = (E[]) new Object[this.data.length]; | |
| boolean changed = false; | |
| int length = 0; | |
| for (E element : snapshot) | |
| { | |
| // copy all the elements, including null values | |
| // if the collection can hold it | |
| // FIXME: slow operation | |
| if (c.contains(element)) | |
| changed = true; | |
| else | |
| storage[length++] = element; | |
| } | |
| if (!changed) | |
| return false; | |
| E[] newData = (E[]) new Object[length]; | |
| System.arraycopy(storage, 0, newData, 0, length); | |
| this.data = newData; | |
| return true; | |
| } | |
| /** | |
| * Removes all the elements that are not in the passed collection. | |
| * If the collection is void, this method has the same effect of | |
| * <code>clear()</code>. | |
| * Please, note that this method is extremely slow (unless the argument has | |
| * <code>size == 0</code>) and has bad performance is both space and time | |
| * usage. | |
| * | |
| * @param c the collection containing the elements to be retained by this | |
| * list. | |
| * @return true the list internal storage changed as a result of this | |
| * operation, false otherwise. | |
| */ | |
| public synchronized boolean retainAll(Collection<?> c) | |
| { | |
| // if the given collection does not contain elements | |
| // we remove all the elements from our storage | |
| if (c.size() == 0) | |
| { | |
| this.clear(); | |
| return true; | |
| } | |
| E [] snapshot = this.data; | |
| E [] storage = (E[]) new Object[this.data.length]; | |
| int length = 0; | |
| for (E element : snapshot) | |
| { | |
| if (c.contains(element)) | |
| storage[length++] = element; | |
| } | |
| // means we retained all the elements previously in our storage | |
| // we are running already slow here, but at least we avoid copying | |
| // another array and changing the internal storage | |
| if (length == snapshot.length) | |
| return false; | |
| E[] newData = (E[]) new Object[length]; | |
| System.arraycopy(storage, 0, newData, 0, length); | |
| this.data = newData; | |
| return true; | |
| } | |
| /** | |
| * Removes all elements from this List | |
| */ | |
| public synchronized void clear() | |
| { | |
| data = (E[]) new Object[0]; | |
| } | |
| /** | |
| * Add each element in the supplied Collection to this List. It is undefined | |
| * what happens if you modify the list while this is taking place; for | |
| * example, if the collection contains this list. c can contain objects of any | |
| * type, as well as null values. | |
| * | |
| * @param c | |
| * a Collection containing elements to be added to this List | |
| * @return true if the list was modified, in other words c is not empty | |
| * @throws NullPointerException | |
| * if c is null | |
| */ | |
| public synchronized boolean addAll(Collection< ? extends E> c) | |
| { | |
| return addAll(data.length, c); | |
| } | |
| /** | |
| * Add all elements in the supplied collection, inserting them beginning at | |
| * the specified index. c can contain objects of any type, as well as null | |
| * values. | |
| * | |
| * @param index | |
| * the index at which the elements will be inserted | |
| * @param c | |
| * the Collection containing the elements to be inserted | |
| * @throws IndexOutOfBoundsException | |
| * if index < 0 || index > 0 | |
| * @throws NullPointerException | |
| * if c is null | |
| */ | |
| public synchronized boolean addAll(int index, Collection< ? extends E> c) | |
| { | |
| if (index < 0 || index > this.size()) | |
| throw new IndexOutOfBoundsException("index = " + index); | |
| int csize = c.size(); | |
| if (csize == 0) | |
| return false; | |
| E[] data = this.data; | |
| Iterator<? extends E> itr = c.iterator(); | |
| E[] newData = (E[]) new Object[data.length + csize]; | |
| // avoid this call at all if we were asked to put the elements at the | |
| // beginning of our storage | |
| if (index != 0) | |
| System.arraycopy(data, 0, newData, 0, index); | |
| int itemsLeft = index; | |
| for (E value : c) | |
| newData[index++] = value; | |
| // now copy the remaining elements | |
| System.arraycopy(data, itemsLeft, newData, 0, data.length - itemsLeft); | |
| this.data = newData; | |
| return true; | |
| } | |
| /** | |
| * Adds an element if the list does not contains it already. | |
| * | |
| * @param val the element to add to the list. | |
| * @return true if the element was added, false otherwise. | |
| */ | |
| public synchronized boolean addIfAbsent(E val) | |
| { | |
| if (contains(val)) | |
| return false; | |
| add(val); | |
| return true; | |
| } | |
| /** | |
| * Adds all the element from the given collection that are not already | |
| * in this list. | |
| * | |
| * @param c the Collection containing the elements to be inserted | |
| * @return true the list internal storage changed as a result of this | |
| * operation, false otherwise. | |
| */ | |
| public synchronized int addAllAbsent(Collection<? extends E> c) | |
| { | |
| int size = c.size(); | |
| if (size == 0) | |
| return 0; | |
| E [] snapshot = this.data; | |
| E [] storage = (E[]) new Object[size]; | |
| size = 0; | |
| for (E val : c) | |
| { | |
| if (!this.contains(val)) | |
| storage[size++] = val; | |
| } | |
| if (size == 0) | |
| return 0; | |
| // append storage to data | |
| E [] newData = (E[]) new Object[snapshot.length + size]; | |
| System.arraycopy(snapshot, 0, newData, 0, snapshot.length); | |
| System.arraycopy(storage, 0, newData, snapshot.length, size); | |
| this.data = newData; | |
| return size; | |
| } | |
| public String toString() | |
| { | |
| return Arrays.toString(this.data); | |
| } | |
| public boolean equals(Object o) | |
| { | |
| if (o == null) | |
| return false; | |
| if (this == o) | |
| return true; | |
| // let's see if 'o' is a list, if so, we need to compare the elements | |
| // as returned by the iterator | |
| if (o instanceof List) | |
| { | |
| List<?> source = (List<?>) o; | |
| if (source.size() != this.size()) | |
| return false; | |
| Iterator<?> sourceIterator = source.iterator(); | |
| for (E element : this) | |
| { | |
| if (!element.equals(sourceIterator.next())) | |
| return false; | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| public int hashCode() | |
| { | |
| // see http://java.sun.com/6/docs/api/java/util/List.html#hashcode() | |
| int hashcode = 1; | |
| for (E element : this) | |
| { | |
| hashcode = 31 * hashcode + (element == null ? 0 : element.hashCode()); | |
| } | |
| return hashcode; | |
| } | |
| /** | |
| * Return an Iterator containing the elements of this list. | |
| * The Iterator uses a snapshot of the state of the internal storage | |
| * at the moment this method is called and does <strong>not</strong> support | |
| * update operations, so no synchronization is needed to traverse the | |
| * iterator. | |
| * | |
| * @return an Iterator containing the elements of this list in sequence. | |
| */ | |
| public Iterator<E> iterator() | |
| { | |
| return new Iterator<E>() | |
| { | |
| E [] iteratorData = CopyOnWriteArrayList.this.data; | |
| int currentElement = 0; | |
| public boolean hasNext() | |
| { | |
| return (currentElement < iteratorData.length); | |
| } | |
| public E next() | |
| { | |
| return iteratorData[currentElement++]; | |
| } | |
| public void remove() | |
| { | |
| throw new UnsupportedOperationException("updating of elements in " + | |
| "iterators is not supported " + | |
| "by this class"); | |
| } | |
| }; | |
| } | |
| /** | |
| * Return a ListIterator containing the elements of this list. | |
| * The Iterator uses a snapshot of the state of the internal storage | |
| * at the moment this method is called and does <strong>not</strong> support | |
| * update operations, so no synchronization is needed to traverse the | |
| * iterator. | |
| * | |
| * @return a ListIterator containing the elements of this list in sequence. | |
| */ | |
| public ListIterator<E> listIterator() | |
| { | |
| return listIterator(0); | |
| } | |
| /** | |
| * Return a ListIterator over the elements of this list starting at | |
| * the specified index. An initial call to {@code next()} will thus | |
| * return the element at {@code index}, while an initial call to | |
| * {@code previous()} will return the element at {@code index-1}. The | |
| * Iterator uses a snapshot of the state of the internal storage | |
| * at the moment this method is called and does <strong>not</strong> support | |
| * update operations, so no synchronization is needed to traverse the | |
| * iterator. | |
| * | |
| * @param index the index at which to start iterating. | |
| * @return a ListIterator containing the elements of this list in sequence. | |
| */ | |
| public ListIterator<E> listIterator(final int index) | |
| { | |
| if (index < 0 || index > size()) | |
| throw new IndexOutOfBoundsException("Index: " + index + ", Size:" | |
| + size()); | |
| return new ListIterator<E>() | |
| { | |
| E [] iteratorData = CopyOnWriteArrayList.this.data; | |
| int currentElement = index; | |
| public void add(E o) | |
| { | |
| throw new UnsupportedOperationException("updating of elements in " + | |
| "iterators is not supported " + | |
| "by this class"); | |
| } | |
| public boolean hasNext() | |
| { | |
| return (currentElement < iteratorData.length); | |
| } | |
| public boolean hasPrevious() | |
| { | |
| return (currentElement > 0); | |
| } | |
| public E next() | |
| { | |
| if (hasNext() == false) | |
| throw new java.util.NoSuchElementException(); | |
| return iteratorData[currentElement++]; | |
| } | |
| public int nextIndex() | |
| { | |
| return (currentElement + 1); | |
| } | |
| public E previous() | |
| { | |
| if (hasPrevious() == false) | |
| throw new java.util.NoSuchElementException(); | |
| return iteratorData[--currentElement]; | |
| } | |
| public int previousIndex() | |
| { | |
| return (currentElement - 1); | |
| } | |
| public void remove() | |
| { | |
| throw new UnsupportedOperationException("updating of elements in " + | |
| "iterators is not supported " + | |
| "by this class"); | |
| } | |
| public void set(E o) | |
| { | |
| throw new UnsupportedOperationException("updating of elements in " + | |
| "iterators is not supported " + | |
| "by this class"); | |
| } | |
| }; | |
| } | |
| /** | |
| * Obtain a List view of a subsection of this list, from fromIndex | |
| * (inclusive) to toIndex (exclusive). If the two indices are equal, the | |
| * sublist is empty. The returned list should be modifiable if and only | |
| * if this list is modifiable. Changes to the returned list should be | |
| * reflected in this list. If this list is structurally modified in | |
| * any way other than through the returned list, the result of any subsequent | |
| * operations on the returned list is undefined. | |
| * <p> | |
| * | |
| * This implementation returns a subclass of AbstractList. It stores, in | |
| * private fields, the offset and size of the sublist, and the expected | |
| * modCount of the backing list. If the backing list implements RandomAccess, | |
| * the sublist will also. | |
| * <p> | |
| * | |
| * The subclass's <code>set(int, Object)</code>, <code>get(int)</code>, | |
| * <code>add(int, Object)</code>, <code>remove(int)</code>, | |
| * <code>addAll(int, Collection)</code> and | |
| * <code>removeRange(int, int)</code> methods all delegate to the | |
| * corresponding methods on the backing abstract list, after | |
| * bounds-checking the index and adjusting for the offset. The | |
| * <code>addAll(Collection c)</code> method merely returns addAll(size, c). | |
| * The <code>listIterator(int)</code> method returns a "wrapper object" | |
| * over a list iterator on the backing list, which is created with the | |
| * corresponding method on the backing list. The <code>iterator()</code> | |
| * method merely returns listIterator(), and the <code>size()</code> method | |
| * merely returns the subclass's size field. | |
| * <p> | |
| * | |
| * All methods first check to see if the actual modCount of the backing | |
| * list is equal to its expected value, and throw a | |
| * ConcurrentModificationException if it is not. | |
| * | |
| * @param fromIndex the index that the returned list should start from | |
| * (inclusive) | |
| * @param toIndex the index that the returned list should go to (exclusive) | |
| * @return a List backed by a subsection of this list | |
| * @throws IndexOutOfBoundsException if fromIndex < 0 | |
| * || toIndex > size() | |
| * @throws IndexOutOfBoundsException if fromIndex > toIndex | |
| * @see ConcurrentModificationException | |
| * @see RandomAccess | |
| */ | |
| public synchronized List<E> subList(int fromIndex, int toIndex) | |
| { | |
| // This follows the specification of AbstractList, but is inconsistent | |
| // with the one in List. Don't you love Sun's inconsistencies? | |
| if (fromIndex > toIndex) | |
| throw new IndexOutOfBoundsException(fromIndex + " > " + toIndex); | |
| if (fromIndex < 0 || toIndex > size()) | |
| throw new IndexOutOfBoundsException(); | |
| if (this instanceof RandomAccess) | |
| return new RandomAccessSubList<E>(this, fromIndex, toIndex); | |
| return new SubList<E>(this, fromIndex, toIndex); | |
| } | |
| /** | |
| * This class follows the implementation requirements set forth in | |
| * {@link AbstractList#subList(int, int)}. It matches Sun's implementation | |
| * by using a non-public top-level class in the same package. | |
| * | |
| * @author Original author unknown | |
| * @author Eric Blake (ebb9@email.byu.edu) | |
| */ | |
| private static class SubList<E> | |
| extends AbstractList<E> | |
| { | |
| // Package visible, for use by iterator. | |
| /** The original list. */ | |
| final CopyOnWriteArrayList<E> backingList; | |
| /** The index of the first element of the sublist. */ | |
| final int offset; | |
| /** The size of the sublist. */ | |
| int size; | |
| /** The backing data */ | |
| E[] data; | |
| /** | |
| * Construct the sublist. | |
| * | |
| * @param backing the list this comes from | |
| * @param fromIndex the lower bound, inclusive | |
| * @param toIndex the upper bound, exclusive | |
| */ | |
| SubList(CopyOnWriteArrayList<E> backing, int fromIndex, int toIndex) | |
| { | |
| backingList = backing; | |
| data = backing.data; | |
| offset = fromIndex; | |
| size = toIndex - fromIndex; | |
| } | |
| /** | |
| * This method checks the two modCount fields to ensure that there has | |
| * not been a concurrent modification, returning if all is okay. | |
| * | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| */ | |
| // This can be inlined. Package visible, for use by iterator. | |
| void checkMod() | |
| { | |
| if (data != backingList.data) | |
| throw new ConcurrentModificationException(); | |
| } | |
| /** | |
| * This method checks that a value is between 0 and size (inclusive). If | |
| * it is not, an exception is thrown. | |
| * | |
| * @param index the value to check | |
| * @throws IndexOutOfBoundsException if index < 0 || index > size() | |
| */ | |
| // This will get inlined, since it is private. | |
| private void checkBoundsInclusive(int index) | |
| { | |
| if (index < 0 || index > size) | |
| throw new IndexOutOfBoundsException("Index: " + index + | |
| ", Size:" + size); | |
| } | |
| /** | |
| * This method checks that a value is between 0 (inclusive) and size | |
| * (exclusive). If it is not, an exception is thrown. | |
| * | |
| * @param index the value to check | |
| * @throws IndexOutOfBoundsException if index < 0 || index >= size() | |
| */ | |
| // This will get inlined, since it is private. | |
| private void checkBoundsExclusive(int index) | |
| { | |
| if (index < 0 || index >= size) | |
| throw new IndexOutOfBoundsException("Index: " + index + | |
| ", Size:" + size); | |
| } | |
| /** | |
| * Specified by AbstractList.subList to return the private field size. | |
| * | |
| * @return the sublist size | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| */ | |
| public int size() | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| return size; | |
| } | |
| } | |
| public void clear() | |
| { | |
| synchronized (backingList) | |
| { | |
| E[] snapshot = backingList.data; | |
| E[] newData = (E[]) new Object[snapshot.length - size]; | |
| int toIndex = size + offset; | |
| System.arraycopy(snapshot, 0, newData, 0, offset); | |
| System.arraycopy(snapshot, toIndex, newData, offset, | |
| snapshot.length - toIndex); | |
| backingList.data = newData; | |
| this.data = backingList.data; | |
| this.size = 0; | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to delegate to the backing list. | |
| * | |
| * @param index the location to modify | |
| * @param o the new value | |
| * @return the old value | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws UnsupportedOperationException if the backing list does not | |
| * support the set operation | |
| * @throws IndexOutOfBoundsException if index < 0 || index >= size() | |
| * @throws ClassCastException if o cannot be added to the backing list due | |
| * to its type | |
| * @throws IllegalArgumentException if o cannot be added to the backing list | |
| * for some other reason | |
| */ | |
| public E set(int index, E o) | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| checkBoundsExclusive(index); | |
| E el = backingList.set(index + offset, o); | |
| this.data = backingList.data; | |
| return el; | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to delegate to the backing list. | |
| * | |
| * @param index the location to get from | |
| * @return the object at that location | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws IndexOutOfBoundsException if index < 0 || index >= size() | |
| */ | |
| public E get(int index) | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| checkBoundsExclusive(index); | |
| return backingList.get(index + offset); | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to delegate to the backing list. | |
| * | |
| * @param index the index to insert at | |
| * @param o the object to add | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws IndexOutOfBoundsException if index < 0 || index > size() | |
| * @throws UnsupportedOperationException if the backing list does not | |
| * support the add operation. | |
| * @throws ClassCastException if o cannot be added to the backing list due | |
| * to its type. | |
| * @throws IllegalArgumentException if o cannot be added to the backing | |
| * list for some other reason. | |
| */ | |
| public void add(int index, E o) | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| checkBoundsInclusive(index); | |
| backingList.add(index + offset, o); | |
| this.data = backingList.data; | |
| size++; | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to delegate to the backing list. | |
| * | |
| * @param index the index to remove | |
| * @return the removed object | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws IndexOutOfBoundsException if index < 0 || index >= size() | |
| * @throws UnsupportedOperationException if the backing list does not | |
| * support the remove operation | |
| */ | |
| public E remove(int index) | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| checkBoundsExclusive(index); | |
| E o = backingList.remove(index + offset); | |
| this.data = backingList.data; | |
| size--; | |
| return o; | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to delegate to the backing list. | |
| * | |
| * @param index the location to insert at | |
| * @param c the collection to insert | |
| * @return true if this list was modified, in other words, c is non-empty | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws IndexOutOfBoundsException if index < 0 || index > size() | |
| * @throws UnsupportedOperationException if this list does not support the | |
| * addAll operation | |
| * @throws ClassCastException if some element of c cannot be added to this | |
| * list due to its type | |
| * @throws IllegalArgumentException if some element of c cannot be added | |
| * to this list for some other reason | |
| * @throws NullPointerException if the specified collection is null | |
| */ | |
| public boolean addAll(int index, Collection<? extends E> c) | |
| { | |
| synchronized (backingList) | |
| { | |
| checkMod(); | |
| checkBoundsInclusive(index); | |
| int csize = c.size(); | |
| boolean result = backingList.addAll(offset + index, c); | |
| this.data = backingList.data; | |
| size += csize; | |
| return result; | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to return addAll(size, c). | |
| * | |
| * @param c the collection to insert | |
| * @return true if this list was modified, in other words, c is non-empty | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws UnsupportedOperationException if this list does not support the | |
| * addAll operation | |
| * @throws ClassCastException if some element of c cannot be added to this | |
| * list due to its type | |
| * @throws IllegalArgumentException if some element of c cannot be added | |
| * to this list for some other reason | |
| * @throws NullPointerException if the specified collection is null | |
| */ | |
| public boolean addAll(Collection<? extends E> c) | |
| { | |
| synchronized (backingList) | |
| { | |
| return addAll(size, c); | |
| } | |
| } | |
| /** | |
| * Specified by AbstractList.subList to return listIterator(). | |
| * | |
| * @return an iterator over the sublist | |
| */ | |
| public Iterator<E> iterator() | |
| { | |
| return listIterator(); | |
| } | |
| /** | |
| * Specified by AbstractList.subList to return a wrapper around the | |
| * backing list's iterator. | |
| * | |
| * @param index the start location of the iterator | |
| * @return a list iterator over the sublist | |
| * @throws ConcurrentModificationException if the backing list has been | |
| * modified externally to this sublist | |
| * @throws IndexOutOfBoundsException if the value is out of range | |
| */ | |
| public ListIterator<E> listIterator(final int index) | |
| { | |
| checkMod(); | |
| checkBoundsInclusive(index); | |
| return new ListIterator<E>() | |
| { | |
| private final ListIterator<E> i = | |
| backingList.listIterator(index + offset); | |
| private int position = index; | |
| /** | |
| * Tests to see if there are any more objects to | |
| * return. | |
| * | |
| * @return True if the end of the list has not yet been | |
| * reached. | |
| */ | |
| public boolean hasNext() | |
| { | |
| return position < size; | |
| } | |
| /** | |
| * Tests to see if there are objects prior to the | |
| * current position in the list. | |
| * | |
| * @return True if objects exist prior to the current | |
| * position of the iterator. | |
| */ | |
| public boolean hasPrevious() | |
| { | |
| return position > 0; | |
| } | |
| /** | |
| * Retrieves the next object from the list. | |
| * | |
| * @return The next object. | |
| * @throws NoSuchElementException if there are no | |
| * more objects to retrieve. | |
| * @throws ConcurrentModificationException if the | |
| * list has been modified elsewhere. | |
| */ | |
| public E next() | |
| { | |
| if (position == size) | |
| throw new NoSuchElementException(); | |
| position++; | |
| return i.next(); | |
| } | |
| /** | |
| * Retrieves the previous object from the list. | |
| * | |
| * @return The next object. | |
| * @throws NoSuchElementException if there are no | |
| * previous objects to retrieve. | |
| * @throws ConcurrentModificationException if the | |
| * list has been modified elsewhere. | |
| */ | |
| public E previous() | |
| { | |
| if (position == 0) | |
| throw new NoSuchElementException(); | |
| position--; | |
| return i.previous(); | |
| } | |
| /** | |
| * Returns the index of the next element in the | |
| * list, which will be retrieved by <code>next()</code> | |
| * | |
| * @return The index of the next element. | |
| */ | |
| public int nextIndex() | |
| { | |
| return i.nextIndex() - offset; | |
| } | |
| /** | |
| * Returns the index of the previous element in the | |
| * list, which will be retrieved by <code>previous()</code> | |
| * | |
| * @return The index of the previous element. | |
| */ | |
| public int previousIndex() | |
| { | |
| return i.previousIndex() - offset; | |
| } | |
| /** | |
| * Removes the last object retrieved by <code>next()</code> | |
| * from the list, if the list supports object removal. | |
| * | |
| * @throws IllegalStateException if the iterator is positioned | |
| * before the start of the list or the last object has already | |
| * been removed. | |
| * @throws UnsupportedOperationException if the list does | |
| * not support removing elements. | |
| */ | |
| public void remove() | |
| { | |
| throw new UnsupportedOperationException("Modification not supported " + | |
| "on CopyOnWriteArrayList iterators"); | |
| } | |
| /** | |
| * Replaces the last object retrieved by <code>next()</code> | |
| * or <code>previous</code> with o, if the list supports object | |
| * replacement and an add or remove operation has not already | |
| * been performed. | |
| * | |
| * @throws IllegalStateException if the iterator is positioned | |
| * before the start of the list or the last object has already | |
| * been removed. | |
| * @throws UnsupportedOperationException if the list doesn't support | |
| * the addition or removal of elements. | |
| * @throws ClassCastException if the type of o is not a valid type | |
| * for this list. | |
| * @throws IllegalArgumentException if something else related to o | |
| * prevents its addition. | |
| * @throws ConcurrentModificationException if the list | |
| * has been modified elsewhere. | |
| */ | |
| public void set(E o) | |
| { | |
| throw new UnsupportedOperationException("Modification not supported " + | |
| "on CopyOnWriteArrayList iterators"); | |
| } | |
| /** | |
| * Adds the supplied object before the element that would be returned | |
| * by a call to <code>next()</code>, if the list supports addition. | |
| * | |
| * @param o The object to add to the list. | |
| * @throws UnsupportedOperationException if the list doesn't support | |
| * the addition of new elements. | |
| * @throws ClassCastException if the type of o is not a valid type | |
| * for this list. | |
| * @throws IllegalArgumentException if something else related to o | |
| * prevents its addition. | |
| * @throws ConcurrentModificationException if the list | |
| * has been modified elsewhere. | |
| */ | |
| public void add(E o) | |
| { | |
| throw new UnsupportedOperationException("Modification not supported " + | |
| "on CopyOnWriteArrayList iterators"); | |
| } | |
| }; | |
| } | |
| } // class SubList | |
| /** | |
| * This class is a RandomAccess version of SubList, as required by | |
| * {@link AbstractList#subList(int, int)}. | |
| * | |
| * @author Eric Blake (ebb9@email.byu.edu) | |
| */ | |
| private static final class RandomAccessSubList<E> extends SubList<E> | |
| implements RandomAccess | |
| { | |
| /** | |
| * Construct the sublist. | |
| * | |
| * @param backing the list this comes from | |
| * @param fromIndex the lower bound, inclusive | |
| * @param toIndex the upper bound, exclusive | |
| */ | |
| RandomAccessSubList(CopyOnWriteArrayList<E> backing, int fromIndex, int toIndex) | |
| { | |
| super(backing, fromIndex, toIndex); | |
| } | |
| } // class RandomAccessSubList | |
| /** | |
| * Serializes this object to the given stream. | |
| * | |
| * @param s | |
| * the stream to write to | |
| * @throws IOException | |
| * if the underlying stream fails | |
| * @serialData the size field (int), the length of the backing array (int), | |
| * followed by its elements (Objects) in proper order. | |
| */ | |
| private void writeObject(ObjectOutputStream s) throws IOException | |
| { | |
| // The 'size' field. | |
| s.defaultWriteObject(); | |
| // We serialize unused list entries to preserve capacity. | |
| int len = data.length; | |
| s.writeInt(len); | |
| // it would be more efficient to just write "size" items, | |
| // this need readObject read "size" items too. | |
| for (int i = 0; i < data.length; i++) | |
| s.writeObject(data[i]); | |
| } | |
| /** | |
| * Deserializes this object from the given stream. | |
| * | |
| * @param s | |
| * the stream to read from | |
| * @throws ClassNotFoundException | |
| * if the underlying stream fails | |
| * @throws IOException | |
| * if the underlying stream fails | |
| * @serialData the size field (int), the length of the backing array (int), | |
| * followed by its elements (Objects) in proper order. | |
| */ | |
| private void readObject(ObjectInputStream s) throws IOException, | |
| ClassNotFoundException | |
| { | |
| // the `size' field. | |
| s.defaultReadObject(); | |
| int capacity = s.readInt(); | |
| data = (E[]) new Object[capacity]; | |
| for (int i = 0; i < capacity; i++) | |
| data[i] = (E) s.readObject(); | |
| } | |
| static final boolean equals(Object o1, Object o2) | |
| { | |
| return o1 == null ? o2 == null : o1.equals(o2); | |
| } | |
| Object[] getArray() | |
| { | |
| return data; | |
| } | |
| } |