jaroslav@1890: /* jaroslav@1890: * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. jaroslav@1890: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1890: * jaroslav@1890: * This code is free software; you can redistribute it and/or modify it jaroslav@1890: * under the terms of the GNU General Public License version 2 only, as jaroslav@1890: * published by the Free Software Foundation. Oracle designates this jaroslav@1890: * particular file as subject to the "Classpath" exception as provided jaroslav@1890: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1890: * jaroslav@1890: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1890: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1890: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1890: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1890: * accompanied this code). jaroslav@1890: * jaroslav@1890: * You should have received a copy of the GNU General Public License version jaroslav@1890: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1890: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1890: * jaroslav@1890: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1890: * or visit www.oracle.com if you need additional information or have any jaroslav@1890: * questions. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: /* jaroslav@1890: * Written by Doug Lea with assistance from members of JCP JSR-166 jaroslav@1890: * Expert Group. Adapted and released, under explicit permission, jaroslav@1890: * from JDK ArrayList.java which carries the following copyright: jaroslav@1890: * jaroslav@1890: * Copyright 1997 by Sun Microsystems, Inc., jaroslav@1890: * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. jaroslav@1890: * All rights reserved. jaroslav@1890: */ jaroslav@1890: jaroslav@1890: package java.util.concurrent; jaroslav@1890: import java.util.*; jaroslav@1890: import java.util.concurrent.locks.*; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * A thread-safe variant of {@link java.util.ArrayList} in which all mutative jaroslav@1890: * operations (add, set, and so on) are implemented by jaroslav@1890: * making a fresh copy of the underlying array. jaroslav@1890: * jaroslav@1890: *

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

All elements are permitted, including null. jaroslav@1890: * jaroslav@1890: *

Memory consistency effects: As with other concurrent jaroslav@1890: * collections, actions in a thread prior to placing an object into a jaroslav@1890: * {@code CopyOnWriteArrayList} jaroslav@1890: * happen-before jaroslav@1890: * actions subsequent to the access or removal of that element from jaroslav@1890: * the {@code CopyOnWriteArrayList} in another thread. jaroslav@1890: * jaroslav@1890: *

This class is a member of the jaroslav@1890: * jaroslav@1890: * Java Collections Framework. jaroslav@1890: * jaroslav@1890: * @since 1.5 jaroslav@1890: * @author Doug Lea jaroslav@1890: * @param the type of elements held in this collection jaroslav@1890: */ jaroslav@1890: public class CopyOnWriteArrayList jaroslav@1890: implements List, RandomAccess, Cloneable, java.io.Serializable { jaroslav@1890: private static final long serialVersionUID = 8673264195747942595L; jaroslav@1890: jaroslav@1890: /** The lock protecting all mutators */ jaroslav@1895: transient ReentrantLock lock = new ReentrantLock(); jaroslav@1890: jaroslav@1890: /** The array, accessed only via getArray/setArray. */ jaroslav@1890: private volatile transient Object[] array; jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Gets the array. Non-private so as to also be accessible jaroslav@1890: * from CopyOnWriteArraySet class. jaroslav@1890: */ jaroslav@1890: final Object[] getArray() { jaroslav@1890: return array; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Sets the array. jaroslav@1890: */ jaroslav@1890: final void setArray(Object[] a) { jaroslav@1890: array = a; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Creates an empty list. jaroslav@1890: */ jaroslav@1890: public CopyOnWriteArrayList() { jaroslav@1890: setArray(new Object[0]); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Creates a list containing the elements of the specified jaroslav@1890: * collection, in the order they are returned by the collection's jaroslav@1890: * iterator. jaroslav@1890: * jaroslav@1890: * @param c the collection of initially held elements jaroslav@1890: * @throws NullPointerException if the specified collection is null jaroslav@1890: */ jaroslav@1890: public CopyOnWriteArrayList(Collection c) { jaroslav@1890: Object[] elements = c.toArray(); jaroslav@1890: // c.toArray might (incorrectly) not return Object[] (see 6260652) jaroslav@1890: if (elements.getClass() != Object[].class) jaroslav@1890: elements = Arrays.copyOf(elements, elements.length, Object[].class); jaroslav@1890: setArray(elements); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Creates a list holding a copy of the given array. jaroslav@1890: * jaroslav@1890: * @param toCopyIn the array (a copy of this array is used as the jaroslav@1890: * internal array) jaroslav@1890: * @throws NullPointerException if the specified array is null jaroslav@1890: */ jaroslav@1890: public CopyOnWriteArrayList(E[] toCopyIn) { jaroslav@1890: setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns the number of elements in this list. jaroslav@1890: * jaroslav@1890: * @return the number of elements in this list jaroslav@1890: */ jaroslav@1890: public int size() { jaroslav@1890: return getArray().length; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns true if this list contains no elements. jaroslav@1890: * jaroslav@1890: * @return true if this list contains no elements jaroslav@1890: */ jaroslav@1890: public boolean isEmpty() { jaroslav@1890: return size() == 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Test for equality, coping with nulls. jaroslav@1890: */ jaroslav@1890: private static boolean eq(Object o1, Object o2) { jaroslav@1890: return (o1 == null ? o2 == null : o1.equals(o2)); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * static version of indexOf, to allow repeated calls without jaroslav@1890: * needing to re-acquire array each time. jaroslav@1890: * @param o element to search for jaroslav@1890: * @param elements the array jaroslav@1890: * @param index first index to search jaroslav@1890: * @param fence one past last index to search jaroslav@1890: * @return index of element, or -1 if absent jaroslav@1890: */ jaroslav@1890: private static int indexOf(Object o, Object[] elements, jaroslav@1890: int index, int fence) { jaroslav@1890: if (o == null) { jaroslav@1890: for (int i = index; i < fence; i++) jaroslav@1890: if (elements[i] == null) jaroslav@1890: return i; jaroslav@1890: } else { jaroslav@1890: for (int i = index; i < fence; i++) jaroslav@1890: if (o.equals(elements[i])) jaroslav@1890: return i; jaroslav@1890: } jaroslav@1890: return -1; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * static version of lastIndexOf. jaroslav@1890: * @param o element to search for jaroslav@1890: * @param elements the array jaroslav@1890: * @param index first index to search jaroslav@1890: * @return index of element, or -1 if absent jaroslav@1890: */ jaroslav@1890: private static int lastIndexOf(Object o, Object[] elements, int index) { jaroslav@1890: if (o == null) { jaroslav@1890: for (int i = index; i >= 0; i--) jaroslav@1890: if (elements[i] == null) jaroslav@1890: return i; jaroslav@1890: } else { jaroslav@1890: for (int i = index; i >= 0; i--) jaroslav@1890: if (o.equals(elements[i])) jaroslav@1890: return i; jaroslav@1890: } jaroslav@1890: return -1; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns true if this list contains the specified element. jaroslav@1890: * More formally, returns true if and only if this list contains jaroslav@1890: * at least one element e such that jaroslav@1890: * (o==null ? e==null : o.equals(e)). jaroslav@1890: * jaroslav@1890: * @param o element whose presence in this list is to be tested jaroslav@1890: * @return true if this list contains the specified element jaroslav@1890: */ jaroslav@1890: public boolean contains(Object o) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: return indexOf(o, elements, 0, elements.length) >= 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public int indexOf(Object o) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: return indexOf(o, elements, 0, elements.length); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns the index of the first occurrence of the specified element in jaroslav@1890: * this list, searching forwards from index, or returns -1 if jaroslav@1890: * the element is not found. jaroslav@1890: * More formally, returns the lowest index i such that jaroslav@1890: * (i >= index && (e==null ? get(i)==null : e.equals(get(i)))), jaroslav@1890: * or -1 if there is no such index. jaroslav@1890: * jaroslav@1890: * @param e element to search for jaroslav@1890: * @param index index to start searching from jaroslav@1890: * @return the index of the first occurrence of the element in jaroslav@1890: * this list at position index or later in the list; jaroslav@1890: * -1 if the element is not found. jaroslav@1890: * @throws IndexOutOfBoundsException if the specified index is negative jaroslav@1890: */ jaroslav@1890: public int indexOf(E e, int index) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: return indexOf(e, elements, index, elements.length); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public int lastIndexOf(Object o) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: return lastIndexOf(o, elements, elements.length - 1); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns the index of the last occurrence of the specified element in jaroslav@1890: * this list, searching backwards from index, or returns -1 if jaroslav@1890: * the element is not found. jaroslav@1890: * More formally, returns the highest index i such that jaroslav@1890: * (i <= index && (e==null ? get(i)==null : e.equals(get(i)))), jaroslav@1890: * or -1 if there is no such index. jaroslav@1890: * jaroslav@1890: * @param e element to search for jaroslav@1890: * @param index index to start searching backwards from jaroslav@1890: * @return the index of the last occurrence of the element at position jaroslav@1890: * less than or equal to index in this list; jaroslav@1890: * -1 if the element is not found. jaroslav@1890: * @throws IndexOutOfBoundsException if the specified index is greater jaroslav@1890: * than or equal to the current size of this list jaroslav@1890: */ jaroslav@1890: public int lastIndexOf(E e, int index) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: return lastIndexOf(e, elements, index); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a shallow copy of this list. (The elements themselves jaroslav@1890: * are not copied.) jaroslav@1890: * jaroslav@1890: * @return a clone of this list jaroslav@1890: */ jaroslav@1890: public Object clone() { jaroslav@1890: try { jaroslav@1890: CopyOnWriteArrayList c = (CopyOnWriteArrayList)(super.clone()); jaroslav@1890: c.resetLock(); jaroslav@1890: return c; jaroslav@1890: } catch (CloneNotSupportedException e) { jaroslav@1890: // this shouldn't happen, since we are Cloneable jaroslav@1890: throw new InternalError(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns an array containing all of the elements in this list jaroslav@1890: * in proper sequence (from first to last element). jaroslav@1890: * jaroslav@1890: *

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

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

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

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

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

jaroslav@1890:      *     String[] y = x.toArray(new String[0]);
jaroslav@1890: * jaroslav@1890: * Note that toArray(new Object[0]) is identical in function to jaroslav@1890: * toArray(). jaroslav@1890: * jaroslav@1890: * @param a the array into which the elements of the list are to jaroslav@1890: * be stored, if it is big enough; otherwise, a new array of the jaroslav@1890: * same runtime type is allocated for this purpose. jaroslav@1890: * @return an array containing all the elements in this list jaroslav@1890: * @throws ArrayStoreException if the runtime type of the specified array jaroslav@1890: * is not a supertype of the runtime type of every element in jaroslav@1890: * this list jaroslav@1890: * @throws NullPointerException if the specified array is null jaroslav@1890: */ jaroslav@1890: @SuppressWarnings("unchecked") jaroslav@1890: public T[] toArray(T a[]) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (a.length < len) jaroslav@1890: return (T[]) Arrays.copyOf(elements, len, a.getClass()); jaroslav@1890: else { jaroslav@1890: System.arraycopy(elements, 0, a, 0, len); jaroslav@1890: if (a.length > len) jaroslav@1890: a[len] = null; jaroslav@1890: return a; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: // Positional Access Operations jaroslav@1890: jaroslav@1890: @SuppressWarnings("unchecked") jaroslav@1890: private E get(Object[] a, int index) { jaroslav@1890: return (E) a[index]; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * {@inheritDoc} jaroslav@1890: * jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public E get(int index) { jaroslav@1890: return get(getArray(), index); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Replaces the element at the specified position in this list with the jaroslav@1890: * specified element. jaroslav@1890: * jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public E set(int index, E element) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: E oldValue = get(elements, index); jaroslav@1890: jaroslav@1890: if (oldValue != element) { jaroslav@1890: int len = elements.length; jaroslav@1890: Object[] newElements = Arrays.copyOf(elements, len); jaroslav@1890: newElements[index] = element; jaroslav@1890: setArray(newElements); jaroslav@1890: } else { jaroslav@1890: // Not quite a no-op; ensures volatile write semantics jaroslav@1890: setArray(elements); jaroslav@1890: } jaroslav@1890: return oldValue; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Appends the specified element to the end of this list. jaroslav@1890: * jaroslav@1890: * @param e element to be appended to this list jaroslav@1890: * @return true (as specified by {@link Collection#add}) jaroslav@1890: */ jaroslav@1890: public boolean add(E e) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: Object[] newElements = Arrays.copyOf(elements, len + 1); jaroslav@1890: newElements[len] = e; jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Inserts the specified element at the specified position in this jaroslav@1890: * list. Shifts the element currently at that position (if any) and jaroslav@1890: * any subsequent elements to the right (adds one to their indices). jaroslav@1890: * jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public void add(int index, E element) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (index > len || index < 0) jaroslav@1890: throw new IndexOutOfBoundsException("Index: "+index+ jaroslav@1890: ", Size: "+len); jaroslav@1890: Object[] newElements; jaroslav@1890: int numMoved = len - index; jaroslav@1890: if (numMoved == 0) jaroslav@1890: newElements = Arrays.copyOf(elements, len + 1); jaroslav@1890: else { jaroslav@1890: newElements = new Object[len + 1]; jaroslav@1890: System.arraycopy(elements, 0, newElements, 0, index); jaroslav@1890: System.arraycopy(elements, index, newElements, index + 1, jaroslav@1890: numMoved); jaroslav@1890: } jaroslav@1890: newElements[index] = element; jaroslav@1890: setArray(newElements); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Removes the element at the specified position in this list. jaroslav@1890: * Shifts any subsequent elements to the left (subtracts one from their jaroslav@1890: * indices). Returns the element that was removed from the list. jaroslav@1890: * jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public E remove(int index) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: E oldValue = get(elements, index); jaroslav@1890: int numMoved = len - index - 1; jaroslav@1890: if (numMoved == 0) jaroslav@1890: setArray(Arrays.copyOf(elements, len - 1)); jaroslav@1890: else { jaroslav@1890: Object[] newElements = new Object[len - 1]; jaroslav@1890: System.arraycopy(elements, 0, newElements, 0, index); jaroslav@1890: System.arraycopy(elements, index + 1, newElements, index, jaroslav@1890: numMoved); jaroslav@1890: setArray(newElements); jaroslav@1890: } jaroslav@1890: return oldValue; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Removes the first occurrence of the specified element from this list, jaroslav@1890: * if it is present. If this list does not contain the element, it is jaroslav@1890: * unchanged. More formally, removes the element with the lowest index jaroslav@1890: * i such that jaroslav@1890: * (o==null ? get(i)==null : o.equals(get(i))) jaroslav@1890: * (if such an element exists). Returns true if this list jaroslav@1890: * contained the specified element (or equivalently, if this list jaroslav@1890: * changed as a result of the call). jaroslav@1890: * jaroslav@1890: * @param o element to be removed from this list, if present jaroslav@1890: * @return true if this list contained the specified element jaroslav@1890: */ jaroslav@1890: public boolean remove(Object o) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (len != 0) { jaroslav@1890: // Copy while searching for element to remove jaroslav@1890: // This wins in the normal case of element being present jaroslav@1890: int newlen = len - 1; jaroslav@1890: Object[] newElements = new Object[newlen]; jaroslav@1890: jaroslav@1890: for (int i = 0; i < newlen; ++i) { jaroslav@1890: if (eq(o, elements[i])) { jaroslav@1890: // found one; copy remaining and exit jaroslav@1890: for (int k = i + 1; k < len; ++k) jaroslav@1890: newElements[k-1] = elements[k]; jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } else jaroslav@1890: newElements[i] = elements[i]; jaroslav@1890: } jaroslav@1890: jaroslav@1890: // special handling for last cell jaroslav@1890: if (eq(o, elements[newlen])) { jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: return false; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Removes from this list all of the elements whose index is between jaroslav@1890: * fromIndex, inclusive, and toIndex, exclusive. jaroslav@1890: * Shifts any succeeding elements to the left (reduces their index). jaroslav@1890: * This call shortens the list by (toIndex - fromIndex) elements. jaroslav@1890: * (If toIndex==fromIndex, this operation has no effect.) jaroslav@1890: * jaroslav@1890: * @param fromIndex index of first element to be removed jaroslav@1890: * @param toIndex index after last element to be removed jaroslav@1890: * @throws IndexOutOfBoundsException if fromIndex or toIndex out of range jaroslav@1890: * ({@code{fromIndex < 0 || toIndex > size() || toIndex < fromIndex}) jaroslav@1890: */ jaroslav@1890: private void removeRange(int fromIndex, int toIndex) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: jaroslav@1890: if (fromIndex < 0 || toIndex > len || toIndex < fromIndex) jaroslav@1890: throw new IndexOutOfBoundsException(); jaroslav@1890: int newlen = len - (toIndex - fromIndex); jaroslav@1890: int numMoved = len - toIndex; jaroslav@1890: if (numMoved == 0) jaroslav@1890: setArray(Arrays.copyOf(elements, newlen)); jaroslav@1890: else { jaroslav@1890: Object[] newElements = new Object[newlen]; jaroslav@1890: System.arraycopy(elements, 0, newElements, 0, fromIndex); jaroslav@1890: System.arraycopy(elements, toIndex, newElements, jaroslav@1890: fromIndex, numMoved); jaroslav@1890: setArray(newElements); jaroslav@1890: } jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Append the element if not present. jaroslav@1890: * jaroslav@1890: * @param e element to be added to this list, if absent jaroslav@1890: * @return true if the element was added jaroslav@1890: */ jaroslav@1890: public boolean addIfAbsent(E e) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: // Copy while checking if already present. jaroslav@1890: // This wins in the most common case where it is not present jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: Object[] newElements = new Object[len + 1]; jaroslav@1890: for (int i = 0; i < len; ++i) { jaroslav@1890: if (eq(e, elements[i])) jaroslav@1890: return false; // exit, throwing away copy jaroslav@1890: else jaroslav@1890: newElements[i] = elements[i]; jaroslav@1890: } jaroslav@1890: newElements[len] = e; jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns true if this list contains all of the elements of the jaroslav@1890: * specified collection. jaroslav@1890: * jaroslav@1890: * @param c collection to be checked for containment in this list jaroslav@1890: * @return true if this list contains all of the elements of the jaroslav@1890: * specified collection jaroslav@1890: * @throws NullPointerException if the specified collection is null jaroslav@1890: * @see #contains(Object) jaroslav@1890: */ jaroslav@1890: public boolean containsAll(Collection c) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: for (Object e : c) { jaroslav@1890: if (indexOf(e, elements, 0, len) < 0) jaroslav@1890: return false; jaroslav@1890: } jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Removes from this list all of its elements that are contained in jaroslav@1890: * the specified collection. This is a particularly expensive operation jaroslav@1890: * in this class because of the need for an internal temporary array. jaroslav@1890: * jaroslav@1890: * @param c collection containing elements to be removed from this list jaroslav@1890: * @return true if this list changed as a result of the call jaroslav@1890: * @throws ClassCastException if the class of an element of this list jaroslav@1890: * is incompatible with the specified collection jaroslav@1890: * (optional) jaroslav@1890: * @throws NullPointerException if this list contains a null element and the jaroslav@1890: * specified collection does not permit null elements jaroslav@1890: * (optional), jaroslav@1890: * or if the specified collection is null jaroslav@1890: * @see #remove(Object) jaroslav@1890: */ jaroslav@1890: public boolean removeAll(Collection c) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (len != 0) { jaroslav@1890: // temp array holds those elements we know we want to keep jaroslav@1890: int newlen = 0; jaroslav@1890: Object[] temp = new Object[len]; jaroslav@1890: for (int i = 0; i < len; ++i) { jaroslav@1890: Object element = elements[i]; jaroslav@1890: if (!c.contains(element)) jaroslav@1890: temp[newlen++] = element; jaroslav@1890: } jaroslav@1890: if (newlen != len) { jaroslav@1890: setArray(Arrays.copyOf(temp, newlen)); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: return false; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Retains only the elements in this list that are contained in the jaroslav@1890: * specified collection. In other words, removes from this list all of jaroslav@1890: * its elements that are not contained in the specified collection. jaroslav@1890: * jaroslav@1890: * @param c collection containing elements to be retained in this list jaroslav@1890: * @return true if this list changed as a result of the call jaroslav@1890: * @throws ClassCastException if the class of an element of this list jaroslav@1890: * is incompatible with the specified collection jaroslav@1890: * (optional) jaroslav@1890: * @throws NullPointerException if this list contains a null element and the jaroslav@1890: * specified collection does not permit null elements jaroslav@1890: * (optional), jaroslav@1890: * or if the specified collection is null jaroslav@1890: * @see #remove(Object) jaroslav@1890: */ jaroslav@1890: public boolean retainAll(Collection c) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (len != 0) { jaroslav@1890: // temp array holds those elements we know we want to keep jaroslav@1890: int newlen = 0; jaroslav@1890: Object[] temp = new Object[len]; jaroslav@1890: for (int i = 0; i < len; ++i) { jaroslav@1890: Object element = elements[i]; jaroslav@1890: if (c.contains(element)) jaroslav@1890: temp[newlen++] = element; jaroslav@1890: } jaroslav@1890: if (newlen != len) { jaroslav@1890: setArray(Arrays.copyOf(temp, newlen)); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: } jaroslav@1890: return false; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Appends all of the elements in the specified collection that jaroslav@1890: * are not already contained in this list, to the end of jaroslav@1890: * this list, in the order that they are returned by the jaroslav@1890: * specified collection's iterator. jaroslav@1890: * jaroslav@1890: * @param c collection containing elements to be added to this list jaroslav@1890: * @return the number of elements added jaroslav@1890: * @throws NullPointerException if the specified collection is null jaroslav@1890: * @see #addIfAbsent(Object) jaroslav@1890: */ jaroslav@1890: public int addAllAbsent(Collection c) { jaroslav@1890: Object[] cs = c.toArray(); jaroslav@1890: if (cs.length == 0) jaroslav@1890: return 0; jaroslav@1890: Object[] uniq = new Object[cs.length]; jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: int added = 0; jaroslav@1890: for (int i = 0; i < cs.length; ++i) { // scan for duplicates jaroslav@1890: Object e = cs[i]; jaroslav@1890: if (indexOf(e, elements, 0, len) < 0 && jaroslav@1890: indexOf(e, uniq, 0, added) < 0) jaroslav@1890: uniq[added++] = e; jaroslav@1890: } jaroslav@1890: if (added > 0) { jaroslav@1890: Object[] newElements = Arrays.copyOf(elements, len + added); jaroslav@1890: System.arraycopy(uniq, 0, newElements, len, added); jaroslav@1890: setArray(newElements); jaroslav@1890: } jaroslav@1890: return added; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Removes all of the elements from this list. jaroslav@1890: * The list will be empty after this call returns. jaroslav@1890: */ jaroslav@1890: public void clear() { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: setArray(new Object[0]); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Appends all of the elements in the specified collection to the end jaroslav@1890: * of this list, in the order that they are returned by the specified jaroslav@1890: * collection's iterator. jaroslav@1890: * jaroslav@1890: * @param c collection containing elements to be added to this list jaroslav@1890: * @return true if this list changed as a result of the call jaroslav@1890: * @throws NullPointerException if the specified collection is null jaroslav@1890: * @see #add(Object) jaroslav@1890: */ jaroslav@1890: public boolean addAll(Collection c) { jaroslav@1890: Object[] cs = c.toArray(); jaroslav@1890: if (cs.length == 0) jaroslav@1890: return false; jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: Object[] newElements = Arrays.copyOf(elements, len + cs.length); jaroslav@1890: System.arraycopy(cs, 0, newElements, len, cs.length); jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Inserts all of the elements in the specified collection into this jaroslav@1890: * list, starting at the specified position. Shifts the element jaroslav@1890: * currently at that position (if any) and any subsequent elements to jaroslav@1890: * the right (increases their indices). The new elements will appear jaroslav@1890: * in this list in the order that they are returned by the jaroslav@1890: * specified collection's iterator. jaroslav@1890: * jaroslav@1890: * @param index index at which to insert the first element jaroslav@1890: * from the specified collection jaroslav@1890: * @param c collection containing elements to be added to this list jaroslav@1890: * @return true if this list changed as a result of the call jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: * @throws NullPointerException if the specified collection is null jaroslav@1890: * @see #add(int,Object) jaroslav@1890: */ jaroslav@1890: public boolean addAll(int index, Collection c) { jaroslav@1890: Object[] cs = c.toArray(); jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (index > len || index < 0) jaroslav@1890: throw new IndexOutOfBoundsException("Index: "+index+ jaroslav@1890: ", Size: "+len); jaroslav@1890: if (cs.length == 0) jaroslav@1890: return false; jaroslav@1890: int numMoved = len - index; jaroslav@1890: Object[] newElements; jaroslav@1890: if (numMoved == 0) jaroslav@1890: newElements = Arrays.copyOf(elements, len + cs.length); jaroslav@1890: else { jaroslav@1890: newElements = new Object[len + cs.length]; jaroslav@1890: System.arraycopy(elements, 0, newElements, 0, index); jaroslav@1890: System.arraycopy(elements, index, jaroslav@1890: newElements, index + cs.length, jaroslav@1890: numMoved); jaroslav@1890: } jaroslav@1890: System.arraycopy(cs, 0, newElements, index, cs.length); jaroslav@1890: setArray(newElements); jaroslav@1890: return true; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Saves the state of the list to a stream (that is, serializes it). jaroslav@1890: * jaroslav@1890: * @serialData The length of the array backing the list is emitted jaroslav@1890: * (int), followed by all of its elements (each an Object) jaroslav@1890: * in the proper order. jaroslav@1890: * @param s the stream jaroslav@1890: */ jaroslav@1890: private void writeObject(java.io.ObjectOutputStream s) jaroslav@1890: throws java.io.IOException{ jaroslav@1890: jaroslav@1890: s.defaultWriteObject(); jaroslav@1890: jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: // Write out array length jaroslav@1890: s.writeInt(elements.length); jaroslav@1890: jaroslav@1890: // Write out all elements in the proper order. jaroslav@1890: for (Object element : elements) jaroslav@1890: s.writeObject(element); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Reconstitutes the list from a stream (that is, deserializes it). jaroslav@1890: * jaroslav@1890: * @param s the stream jaroslav@1890: */ jaroslav@1890: private void readObject(java.io.ObjectInputStream s) jaroslav@1890: throws java.io.IOException, ClassNotFoundException { jaroslav@1890: jaroslav@1890: s.defaultReadObject(); jaroslav@1890: jaroslav@1890: // bind to new lock jaroslav@1890: resetLock(); jaroslav@1890: jaroslav@1890: // Read in array length and allocate array jaroslav@1890: int len = s.readInt(); jaroslav@1890: Object[] elements = new Object[len]; jaroslav@1890: jaroslav@1890: // Read in all elements in the proper order. jaroslav@1890: for (int i = 0; i < len; i++) jaroslav@1890: elements[i] = s.readObject(); jaroslav@1890: setArray(elements); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a string representation of this list. The string jaroslav@1890: * representation consists of the string representations of the list's jaroslav@1890: * elements in the order they are returned by its iterator, enclosed in jaroslav@1890: * square brackets ("[]"). Adjacent elements are separated by jaroslav@1890: * the characters ", " (comma and space). Elements are jaroslav@1890: * converted to strings as by {@link String#valueOf(Object)}. jaroslav@1890: * jaroslav@1890: * @return a string representation of this list jaroslav@1890: */ jaroslav@1890: public String toString() { jaroslav@1890: return Arrays.toString(getArray()); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Compares the specified object with this list for equality. jaroslav@1890: * Returns {@code true} if the specified object is the same object jaroslav@1890: * as this object, or if it is also a {@link List} and the sequence jaroslav@1890: * of elements returned by an {@linkplain List#iterator() iterator} jaroslav@1890: * over the specified list is the same as the sequence returned by jaroslav@1890: * an iterator over this list. The two sequences are considered to jaroslav@1890: * be the same if they have the same length and corresponding jaroslav@1890: * elements at the same position in the sequence are equal. jaroslav@1890: * Two elements {@code e1} and {@code e2} are considered jaroslav@1890: * equal if {@code (e1==null ? e2==null : e1.equals(e2))}. jaroslav@1890: * jaroslav@1890: * @param o the object to be compared for equality with this list jaroslav@1890: * @return {@code true} if the specified object is equal to this list jaroslav@1890: */ jaroslav@1890: public boolean equals(Object o) { jaroslav@1890: if (o == this) jaroslav@1890: return true; jaroslav@1890: if (!(o instanceof List)) jaroslav@1890: return false; jaroslav@1890: jaroslav@1890: List list = (List)(o); jaroslav@1890: Iterator it = list.iterator(); jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: for (int i = 0; i < len; ++i) jaroslav@1890: if (!it.hasNext() || !eq(elements[i], it.next())) jaroslav@1890: return false; jaroslav@1890: if (it.hasNext()) jaroslav@1890: return false; jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns the hash code value for this list. jaroslav@1890: * jaroslav@1890: *

This implementation uses the definition in {@link List#hashCode}. jaroslav@1890: * jaroslav@1890: * @return the hash code value for this list jaroslav@1890: */ jaroslav@1890: public int hashCode() { jaroslav@1890: int hashCode = 1; jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: for (int i = 0; i < len; ++i) { jaroslav@1890: Object obj = elements[i]; jaroslav@1890: hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); jaroslav@1890: } jaroslav@1890: return hashCode; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns an iterator over the elements in this list in proper sequence. jaroslav@1890: * jaroslav@1890: *

The returned iterator provides a snapshot of the state of the list jaroslav@1890: * when the iterator was constructed. No synchronization is needed while jaroslav@1890: * traversing the iterator. The iterator does NOT support the jaroslav@1890: * remove method. jaroslav@1890: * jaroslav@1890: * @return an iterator over the elements in this list in proper sequence jaroslav@1890: */ jaroslav@1890: public Iterator iterator() { jaroslav@1890: return new COWIterator(getArray(), 0); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * {@inheritDoc} jaroslav@1890: * jaroslav@1890: *

The returned iterator provides a snapshot of the state of the list jaroslav@1890: * when the iterator was constructed. No synchronization is needed while jaroslav@1890: * traversing the iterator. The iterator does NOT support the jaroslav@1890: * remove, set or add methods. jaroslav@1890: */ jaroslav@1890: public ListIterator listIterator() { jaroslav@1890: return new COWIterator(getArray(), 0); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * {@inheritDoc} jaroslav@1890: * jaroslav@1890: *

The returned iterator provides a snapshot of the state of the list jaroslav@1890: * when the iterator was constructed. No synchronization is needed while jaroslav@1890: * traversing the iterator. The iterator does NOT support the jaroslav@1890: * remove, set or add methods. jaroslav@1890: * jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public ListIterator listIterator(final int index) { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (index<0 || index>len) jaroslav@1890: throw new IndexOutOfBoundsException("Index: "+index); jaroslav@1890: jaroslav@1890: return new COWIterator(elements, index); jaroslav@1890: } jaroslav@1890: jaroslav@1890: private static class COWIterator implements ListIterator { jaroslav@1890: /** Snapshot of the array */ jaroslav@1890: private final Object[] snapshot; jaroslav@1890: /** Index of element to be returned by subsequent call to next. */ jaroslav@1890: private int cursor; jaroslav@1890: jaroslav@1890: private COWIterator(Object[] elements, int initialCursor) { jaroslav@1890: cursor = initialCursor; jaroslav@1890: snapshot = elements; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public boolean hasNext() { jaroslav@1890: return cursor < snapshot.length; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public boolean hasPrevious() { jaroslav@1890: return cursor > 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: @SuppressWarnings("unchecked") jaroslav@1890: public E next() { jaroslav@1890: if (! hasNext()) jaroslav@1890: throw new NoSuchElementException(); jaroslav@1890: return (E) snapshot[cursor++]; jaroslav@1890: } jaroslav@1890: jaroslav@1890: @SuppressWarnings("unchecked") jaroslav@1890: public E previous() { jaroslav@1890: if (! hasPrevious()) jaroslav@1890: throw new NoSuchElementException(); jaroslav@1890: return (E) snapshot[--cursor]; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public int nextIndex() { jaroslav@1890: return cursor; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public int previousIndex() { jaroslav@1890: return cursor-1; jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Not supported. Always throws UnsupportedOperationException. jaroslav@1890: * @throws UnsupportedOperationException always; remove jaroslav@1890: * is not supported by this iterator. jaroslav@1890: */ jaroslav@1890: public void remove() { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Not supported. Always throws UnsupportedOperationException. jaroslav@1890: * @throws UnsupportedOperationException always; set jaroslav@1890: * is not supported by this iterator. jaroslav@1890: */ jaroslav@1890: public void set(E e) { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Not supported. Always throws UnsupportedOperationException. jaroslav@1890: * @throws UnsupportedOperationException always; add jaroslav@1890: * is not supported by this iterator. jaroslav@1890: */ jaroslav@1890: public void add(E e) { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Returns a view of the portion of this list between jaroslav@1890: * fromIndex, inclusive, and toIndex, exclusive. jaroslav@1890: * The returned list is backed by this list, so changes in the jaroslav@1890: * returned list are reflected in this list. jaroslav@1890: * jaroslav@1890: *

The semantics of the list returned by this method become jaroslav@1890: * undefined if the backing list (i.e., this list) is modified in jaroslav@1890: * any way other than via the returned list. jaroslav@1890: * jaroslav@1890: * @param fromIndex low endpoint (inclusive) of the subList jaroslav@1890: * @param toIndex high endpoint (exclusive) of the subList jaroslav@1890: * @return a view of the specified range within this list jaroslav@1890: * @throws IndexOutOfBoundsException {@inheritDoc} jaroslav@1890: */ jaroslav@1890: public List subList(int fromIndex, int toIndex) { jaroslav@1890: final ReentrantLock lock = this.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: Object[] elements = getArray(); jaroslav@1890: int len = elements.length; jaroslav@1890: if (fromIndex < 0 || toIndex > len || fromIndex > toIndex) jaroslav@1890: throw new IndexOutOfBoundsException(); jaroslav@1890: return new COWSubList(this, fromIndex, toIndex); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: /** jaroslav@1890: * Sublist for CopyOnWriteArrayList. jaroslav@1890: * This class extends AbstractList merely for convenience, to jaroslav@1890: * avoid having to define addAll, etc. This doesn't hurt, but jaroslav@1890: * is wasteful. This class does not need or use modCount jaroslav@1890: * mechanics in AbstractList, but does need to check for jaroslav@1890: * concurrent modification using similar mechanics. On each jaroslav@1890: * operation, the array that we expect the backing list to use jaroslav@1890: * is checked and updated. Since we do this for all of the jaroslav@1890: * base operations invoked by those defined in AbstractList, jaroslav@1890: * all is well. While inefficient, this is not worth jaroslav@1890: * improving. The kinds of list operations inherited from jaroslav@1890: * AbstractList are already so slow on COW sublists that jaroslav@1890: * adding a bit more space/time doesn't seem even noticeable. jaroslav@1890: */ jaroslav@1890: private static class COWSubList jaroslav@1890: extends AbstractList jaroslav@1890: implements RandomAccess jaroslav@1890: { jaroslav@1890: private final CopyOnWriteArrayList l; jaroslav@1890: private final int offset; jaroslav@1890: private int size; jaroslav@1890: private Object[] expectedArray; jaroslav@1890: jaroslav@1890: // only call this holding l's lock jaroslav@1890: COWSubList(CopyOnWriteArrayList list, jaroslav@1890: int fromIndex, int toIndex) { jaroslav@1890: l = list; jaroslav@1890: expectedArray = l.getArray(); jaroslav@1890: offset = fromIndex; jaroslav@1890: size = toIndex - fromIndex; jaroslav@1890: } jaroslav@1890: jaroslav@1890: // only call this holding l's lock jaroslav@1890: private void checkForComodification() { jaroslav@1890: if (l.getArray() != expectedArray) jaroslav@1890: throw new ConcurrentModificationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: // only call this holding l's lock jaroslav@1890: private void rangeCheck(int index) { jaroslav@1890: if (index<0 || index>=size) jaroslav@1890: throw new IndexOutOfBoundsException("Index: "+index+ jaroslav@1890: ",Size: "+size); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public E set(int index, E element) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: rangeCheck(index); jaroslav@1890: checkForComodification(); jaroslav@1890: E x = l.set(index+offset, element); jaroslav@1890: expectedArray = l.getArray(); jaroslav@1890: return x; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public E get(int index) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: rangeCheck(index); jaroslav@1890: checkForComodification(); jaroslav@1890: return l.get(index+offset); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public int size() { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: return size; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public void add(int index, E element) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: if (index<0 || index>size) jaroslav@1890: throw new IndexOutOfBoundsException(); jaroslav@1890: l.add(index+offset, element); jaroslav@1890: expectedArray = l.getArray(); jaroslav@1890: size++; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public void clear() { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: l.removeRange(offset, offset+size); jaroslav@1890: expectedArray = l.getArray(); jaroslav@1890: size = 0; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public E remove(int index) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: rangeCheck(index); jaroslav@1890: checkForComodification(); jaroslav@1890: E result = l.remove(index+offset); jaroslav@1890: expectedArray = l.getArray(); jaroslav@1890: size--; jaroslav@1890: return result; jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public boolean remove(Object o) { jaroslav@1890: int index = indexOf(o); jaroslav@1890: if (index == -1) jaroslav@1890: return false; jaroslav@1890: remove(index); jaroslav@1890: return true; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public Iterator iterator() { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: return new COWSubListIterator(l, 0, offset, size); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public ListIterator listIterator(final int index) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: if (index<0 || index>size) jaroslav@1890: throw new IndexOutOfBoundsException("Index: "+index+ jaroslav@1890: ", Size: "+size); jaroslav@1890: return new COWSubListIterator(l, index, offset, size); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: public List subList(int fromIndex, int toIndex) { jaroslav@1890: final ReentrantLock lock = l.lock; jaroslav@1890: lock.lock(); jaroslav@1890: try { jaroslav@1890: checkForComodification(); jaroslav@1890: if (fromIndex<0 || toIndex>size) jaroslav@1890: throw new IndexOutOfBoundsException(); jaroslav@1890: return new COWSubList(l, fromIndex + offset, jaroslav@1890: toIndex + offset); jaroslav@1890: } finally { jaroslav@1890: lock.unlock(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: } jaroslav@1890: jaroslav@1890: jaroslav@1890: private static class COWSubListIterator implements ListIterator { jaroslav@1890: private final ListIterator i; jaroslav@1890: private final int index; jaroslav@1890: private final int offset; jaroslav@1890: private final int size; jaroslav@1890: jaroslav@1890: COWSubListIterator(List l, int index, int offset, jaroslav@1890: int size) { jaroslav@1890: this.index = index; jaroslav@1890: this.offset = offset; jaroslav@1890: this.size = size; jaroslav@1890: i = l.listIterator(index+offset); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public boolean hasNext() { jaroslav@1890: return nextIndex() < size; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public E next() { jaroslav@1890: if (hasNext()) jaroslav@1890: return i.next(); jaroslav@1890: else jaroslav@1890: throw new NoSuchElementException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public boolean hasPrevious() { jaroslav@1890: return previousIndex() >= 0; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public E previous() { jaroslav@1890: if (hasPrevious()) jaroslav@1890: return i.previous(); jaroslav@1890: else jaroslav@1890: throw new NoSuchElementException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public int nextIndex() { jaroslav@1890: return i.nextIndex() - offset; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public int previousIndex() { jaroslav@1890: return i.previousIndex() - offset; jaroslav@1890: } jaroslav@1890: jaroslav@1890: public void remove() { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public void set(E e) { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: jaroslav@1890: public void add(E e) { jaroslav@1890: throw new UnsupportedOperationException(); jaroslav@1890: } jaroslav@1890: } jaroslav@1890: jaroslav@1890: // Support for resetting lock while deserializing jaroslav@1890: private void resetLock() { jaroslav@1895: this.lock = new ReentrantLock(); jaroslav@1890: } jaroslav@1890: }