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 extends E> 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 extends E> 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 extends E> 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 extends E> 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: }