A variant of java.util.ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array
/*
* File: CopyOnWriteArrayList.java
*
* Written by Doug Lea. Adapted and released, under explicit permission, from
* JDK1.2 ArrayList.java which carries the following copyright:
*
* Copyright 1997 by Sun Microsystems, Inc., 901 San Antonio Road, Palo Alto,
* California, 94303, U.S.A. All rights reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not disclose such
* Confidential Information and shall use it only in accordance with the terms
* of the license agreement you entered into with Sun.
*
* History: Date Who What 21Jun1998 dl Create public version 9Oct1999 dl faster
* equals 29jun2001 dl Serialization methods now private
*/
import java.util.*;
/**
* This class implements a variant of java.util.ArrayList in which all mutative
* operations (add, set, and so on) are implemented by making a fresh copy of
* the underlying array.
* <p>
* This is ordinarily too costly, but it becomes attractive when traversal
* operations vastly overwhelm mutations, and, especially, when you cannot or
* don't want to synchronize traversals, yet need to preclude interference among
* concurrent threads. The iterator method uses a reference to the state of the
* array at the point that the iterator was created. This array never changes
* during the lifetime of the iterator, so interference is impossible. (The
* iterator will not traverse elements added or changed since the iterator was
* created, but usually this is a desirable feature.)
* <p>
* As much code and documentation as possible was shamelessly copied from
* java.util.ArrayList (Thanks, Josh!), with the intent of preserving all
* semantics of ArrayList except for the copy-on-write property. (The java.util
* collection code could not be subclassed here since all of the existing
* collection classes assume elementwise mutability.)
* <p>
* Because of the copy-on-write policy, some one-by-one mutative operations in
* the java.util.Arrays and java.util.Collections classes are so time/space
* intensive as to never be worth calling (except perhaps as benchmarks for
* garbage collectors :-).
* <p>
* Three methods are supported in addition to those described in List and
* ArrayList. The addIfAbsent and addAllAbsent methods provide Set semantics for
* add, and are used in CopyOnWriteArraySet. However, they can also be used
* directly from this List version. The copyIn method (and a constructor that
* invokes it) allow you to copy in an initial array to use. This method can be
* useful when you first want to perform many operations on a plain array, and
* then make a copy available for use through the collection API.
* <p>
* Due to their strict read-only nature, element-changing operations on
* iterators (remove, set, and add) are not supported. These are the only
* methods throwing UnsupportedOperationException.
* <p>
* <p>[<a
* href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
* Introduction to this package. </a>]
*
* @see CopyOnWriteArraySet
*/
public class CopyOnWriteArrayList implements List, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 1L;
/**
* The held array. Directly access only within synchronized methods
*/
protected transient Object[] array_;
/**
* Accessor to the array intended to be called from within unsynchronized
* read-only methods
*
* @return The internal array
*/
protected synchronized Object[] array()
{
return array_;
}
/**
* Constructs an empty list
*
*/
public CopyOnWriteArrayList()
{
array_ = new Object[0];
}
/**
* Constructs an list containing the elements of the specified Collection,
* in the order they are returned by the Collection's iterator.
*
* @param c The collection to get the objects from.
*/
public CopyOnWriteArrayList(Collection c)
{
array_ = new Object[c.size()];
Iterator i = c.iterator();
int size = 0;
while (i.hasNext())
array_[size++] = i.next();
}
/**
* Create a new CopyOnWriteArrayList holding a copy of given array
*
* @param toCopyIn
* the array. A copy of this array is used as the internal array.
*/
public CopyOnWriteArrayList(Object[] toCopyIn)
{
copyIn(toCopyIn, 0, toCopyIn.length);
}
/**
* Replace the held array with a copy of the <code>n</code> elements of
* the provided array, starting at position <code>first</code>. To copy
* an entire array, call with arguments (array, 0, array.length).
*
* @param toCopyIn
* the array. A copy of the indicated elements of this array is
* used as the internal array.
* @param first
* The index of first position of the array to start copying
* from.
* @param n
* the number of elements to copy. This will be the new size of
* the list.
*/
public synchronized void copyIn(Object[] toCopyIn, int first, int n)
{
array_ = new Object[n];
System.arraycopy(toCopyIn, first, array_, 0, n);
}
/**
* Returns the number of components in this list.
*
* @return the number of components in this list.
*/
public int size()
{
return array().length;
}
/**
* Tests if this list has no components.
*
* @return <code>true</code> if this list has no components;
* <code>false</code> otherwise.
*/
public boolean isEmpty()
{
return size() == 0;
}
/**
* Returns true if this list contains the specified element.
*
* @param o
* element whose presence in this List is to be tested.
*/
public boolean contains(Object elem)
{
Object[] elementData = array();
int len = elementData.length;
return indexOf(elem, elementData, len) >= 0;
}
/**
* Searches for the first occurence of the given argument, testing for
* equality using the <code>equals</code> method.
*
* @param elem
* an object.
* @return the index of the first occurrence of the argument in this list;
* returns <code>-1</code> if the object is not found.
* @see Object#equals(Object)
*/
public int indexOf(Object elem)
{
Object[] elementData = array();
int len = elementData.length;
return indexOf(elem, elementData, len);
}
/**
* static version allows repeated call without needed to grab lock for array
* each time
* @param elem
* @param elementData
* @param len
* @return The index that is found. -1 if not found
*/
protected static int indexOf(Object elem, Object[] elementData, int len)
{
if (elem == null)
{
for (int i = 0; i < len; i++)
if (elementData[i] == null)
return i;
}
else
{
for (int i = 0; i < len; i++)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
/**
* Searches for the first occurence of the given argument, beginning the
* search at <code>index</code>, and testing for equality using the
* <code>equals</code> method.
*
* @param elem
* an object.
* @param index
* the index to start searching from.
* @return the index of the first occurrence of the object argument in this
* List at position <code>index</code> or later in the List;
* returns <code>-1</code> if the object is not found.
* @see Object#equals(Object)
*/
// needed in order to compile on 1.2b3
public int indexOf(Object elem, int index)
{
Object[] elementData = array();
int elementCount = elementData.length;
if (elem == null)
{
for (int i = index; i < elementCount; i++)
if (elementData[i] == null)
return i;
}
else
{
for (int i = index; i < elementCount; i++)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
/**
* Returns the index of the last occurrence of the specified object in this
* list.
*
* @param elem
* the desired component.
* @return the index of the last occurrence of the specified object in this
* list; returns -1 if the object is not found.
*/
public int lastIndexOf(Object elem)
{
Object[] elementData = array();
int len = elementData.length;
return lastIndexOf(elem, elementData, len);
}
protected static int lastIndexOf(Object elem, Object[] elementData, int len)
{
if (elem == null)
{
for (int i = len - 1; i >= 0; i--)
if (elementData[i] == null)
return i;
}
else
{
for (int i = len - 1; i >= 0; i--)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
/**
* Searches backwards for the specified object, starting from the specified
* index, and returns an index to it.
*
* @param elem
* the desired component.
* @param index
* the index to start searching from.
* @return the index of the last occurrence of the specified object in this
* List at position less than index in the List; -1 if the object is
* not found.
*/
public int lastIndexOf(Object elem, int index)
{
// needed in order to compile on 1.2b3
Object[] elementData = array();
if (elem == null)
{
for (int i = index; i >= 0; i--)
if (elementData[i] == null)
return i;
}
else
{
for (int i = index; i >= 0; i--)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
/**
* Returns a shallow copy of this list. (The elements themselves are not
* copied.)
*
* @return a clone of this list.
*/
public Object clone()
{
try
{
Object[] elementData = array();
CopyOnWriteArrayList v = (CopyOnWriteArrayList)super.clone();
v.array_ = new Object[elementData.length];
System.arraycopy(elementData, 0, v.array_, 0, elementData.length);
return v;
}
catch (CloneNotSupportedException e)
{
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/**
* Returns an array containing all of the elements in this list in the
* correct order.
*/
public Object[] toArray()
{
Object[] elementData = array();
Object[] result = new Object[elementData.length];
System.arraycopy(elementData, 0, result, 0, elementData.length);
return result;
}
/**
* Returns an array containing all of the elements in this list in the
* correct order. The runtime type of the returned array is that of the
* specified array. If the list fits in the specified array, it is returned
* therein. Otherwise, a new array is allocated with the runtime type of the
* specified array and the size of this list.
* <p>
* If the list fits in the specified array with room to spare (i.e., the
* array has more elements than the list), the element in the array
* immediately following the end of the collection is set to null. This is
* useful in determining the length of the list <em>only</em> if the
* caller knows that the list does not contain any null elements.
*
* @param a
* the array into which the elements of the list are to be
* stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose.
* @return an array containing the elements of the list.
* @exception ArrayStoreException
* the runtime type of a is not a supertype of the runtime
* type of every element in this list.
*/
public Object[] toArray(Object a[])
{
Object[] elementData = array();
if (a.length < elementData.length)
a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),
elementData.length);
System.arraycopy(elementData, 0, a, 0, elementData.length);
if (a.length > elementData.length)
a[elementData.length] = null;
return a;
}
// Positional Access Operations
/**
* Returns the element at the specified position in this list.
*
* @param index
* index of element to return.
* @exception IndexOutOfBoundsException
* index is out of range (index < 0 || index >=
* size()).
*/
public Object get(int index)
{
Object[] elementData = array();
rangeCheck(index, elementData.length);
return elementData[index];
}
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index
* index of element to replace.
* @param element
* element to be stored at the specified position.
* @return the element previously at the specified position.
* @exception IndexOutOfBoundsException
* index out of range (index < 0 || index >= size()).
*/
public synchronized Object set(int index, Object element)
{
int len = array_.length;
rangeCheck(index, len);
Object oldValue = array_[index];
boolean same = (oldValue == element || (element != null && element.equals(oldValue)));
if (!same)
{
Object[] newArray = new Object[len];
System.arraycopy(array_, 0, newArray, 0, len);
newArray[index] = element;
array_ = newArray;
}
return oldValue;
}
/**
* Appends the specified element to the end of this list.
*
* @param element
* element to be appended to this list.
* @return true (as per the general contract of Collection.add).
*/
public synchronized boolean add(Object element)
{
int len = array_.length;
Object[] newArray = new Object[len + 1];
System.arraycopy(array_, 0, newArray, 0, len);
newArray[len] = element;
array_ = newArray;
return true;
}
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any subsequent
* elements to the right (adds one to their indices).
*
* @param index
* index at which the specified element is to be inserted.
* @param element
* element to be inserted.
* @exception IndexOutOfBoundsException
* index is out of range (index < 0 || index > size()).
*/
public synchronized void add(int index, Object element)
{
int len = array_.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + len);
Object[] newArray = new Object[len + 1];
System.arraycopy(array_, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array_, index, newArray, index + 1, len - index);
array_ = newArray;
}
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index
* the index of the element to removed.
* @exception IndexOutOfBoundsException
* index out of range (index < 0 || index >= size()).
*/
public synchronized Object remove(int index)
{
int len = array_.length;
rangeCheck(index, len);
Object oldValue = array_[index];
Object[] newArray = new Object[len - 1];
System.arraycopy(array_, 0, newArray, 0, index);
int numMoved = len - index - 1;
if (numMoved > 0)
System.arraycopy(array_, index + 1, newArray, index, numMoved);
array_ = newArray;
return oldValue;
}
/**
* Removes a single instance of the specified element from this Collection,
* if it is present (optional operation). More formally, removes an element
* <code>e</code> such that <code>(o==null ? e==null :
* o.equals(e))</code>,
* if the Collection contains one or more such elements. Returns true if the
* Collection contained the specified element (or equivalently, if the
* Collection changed as a result of the call).
*
* @param element
* element to be removed from this Collection, if present.
* @return true if the Collection changed as a result of the call.
*/
public synchronized boolean remove(Object element)
{
int len = array_.length;
if (len == 0)
return false;
// Copy while searching for element to remove
// This wins in the normal case of element being present
int newlen = len - 1;
Object[] newArray = new Object[newlen];
for (int i = 0; i < newlen; ++i)
{
if (element == array_[i] || (element != null && element.equals(array_[i])))
{
// found one; copy remaining and exit
for (int k = i + 1; k < len; ++k)
newArray[k - 1] = array_[k];
array_ = newArray;
return true;
}
else
newArray[i] = array_[i];
}
// special handling for last cell
if (element == array_[newlen] || (element != null && element.equals(array_[newlen])))
{
array_ = newArray;
return true;
}
else
return false; // throw away copy
}
/**
* Removes from this List all of the elements whose index is between
* fromIndex, inclusive and toIndex, exclusive. Shifts any succeeding
* elements to the left (reduces their index). This call shortens the List
* by (toIndex - fromIndex) elements. (If toIndex==fromIndex, this operation
* has no effect.)
*
* @param fromIndex
* index of first element to be removed.
* @param toIndex
* index after last element to be removed.
* @exception IndexOutOfBoundsException
* fromIndex or toIndex out of range (fromIndex < 0 ||
* fromIndex >= size() || toIndex > size() || toIndex
* < fromIndex).
*/
public synchronized void removeRange(int fromIndex, int toIndex)
{
int len = array_.length;
if (fromIndex < 0 || fromIndex >= len || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int numMoved = len - toIndex;
int newlen = len - (toIndex - fromIndex);
Object[] newArray = new Object[newlen];
System.arraycopy(array_, 0, newArray, 0, fromIndex);
System.arraycopy(array_, toIndex, newArray, fromIndex, numMoved);
array_ = newArray;
}
/**
* Append the element if not present. This operation can be used to obtain
* Set semantics for lists.
*
* @param element
* element to be added to this Collection, if absent.
* @return true if added
*/
public synchronized boolean addIfAbsent(Object element)
{
// Copy while checking if already present.
// This wins in the most common case where it is not present
int len = array_.length;
Object[] newArray = new Object[len + 1];
for (int i = 0; i < len; ++i)
{
if (element == array_[i] || (element != null && element.equals(array_[i])))
return false; // exit, throwing away copy
else
newArray[i] = array_[i];
}
newArray[len] = element;
array_ = newArray;
return true;
}
/**
* Returns true if this Collection contains all of the elements in the
* specified Collection.
* <p>
* This implementation iterates over the specified Collection, checking each
* element returned by the Iterator in turn to see if it's contained in this
* Collection. If all elements are so contained true is returned, otherwise
* false.
*
*/
public boolean containsAll(Collection c)
{
Object[] elementData = array();
int len = elementData.length;
Iterator e = c.iterator();
while (e.hasNext())
if (indexOf(e.next(), elementData, len) < 0)
return false;
return true;
}
/**
* Removes from this Collection all of its elements that are contained in
* the specified Collection. This is a particularly expensive operation in
* this class because of the need for an internal temporary array.
* <p>
*
* @return true if this Collection changed as a result of the call.
*/
public synchronized boolean removeAll(Collection c)
{
Object[] elementData = array_;
int len = elementData.length;
if (len == 0)
return false;
// temp array holds those elements we know we want to keep
Object[] temp = new Object[len];
int newlen = 0;
for (int i = 0; i < len; ++i)
{
Object element = elementData[i];
if (!c.contains(element))
{
temp[newlen++] = element;
}
}
if (newlen == len)
return false;
// copy temp as new array
Object[] newArray = new Object[newlen];
System.arraycopy(temp, 0, newArray, 0, newlen);
array_ = newArray;
return true;
}
/**
* Retains only the elements in this Collection that are contained in the
* specified Collection (optional operation). In other words, removes from
* this Collection all of its elements that are not contained in the
* specified Collection.
*
* @return true if this Collection changed as a result of the call.
*/
public synchronized boolean retainAll(Collection c)
{
Object[] elementData = array_;
int len = elementData.length;
if (len == 0)
return false;
Object[] temp = new Object[len];
int newlen = 0;
for (int i = 0; i < len; ++i)
{
Object element = elementData[i];
if (c.contains(element))
{
temp[newlen++] = element;
}
}
if (newlen == len)
return false;
Object[] newArray = new Object[newlen];
System.arraycopy(temp, 0, newArray, 0, newlen);
array_ = newArray;
return true;
}
/**
* Appends all of the elements in the specified Collection that are not
* already contained in this list, to the end of this list, in the order
* that they are returned by the specified Collection's Iterator.
*
* @param c
* elements to be added into this list.
* @return the number of elements added
*/
public synchronized int addAllAbsent(Collection c)
{
int numNew = c.size();
if (numNew == 0)
return 0;
Object[] elementData = array_;
int len = elementData.length;
Object[] temp = new Object[numNew];
int added = 0;
Iterator e = c.iterator();
while (e.hasNext())
{
Object element = e.next();
if (indexOf(element, elementData, len) < 0)
{
if (indexOf(element, temp, added) < 0)
{
temp[added++] = element;
}
}
}
if (added == 0)
return 0;
Object[] newArray = new Object[len + added];
System.arraycopy(elementData, 0, newArray, 0, len);
System.arraycopy(temp, 0, newArray, len, added);
array_ = newArray;
return added;
}
/**
* Removes all of the elements from this list.
*
*/
public synchronized void clear()
{
array_ = new Object[0];
}
/**
* Appends all of the elements in the specified Collection to the end of
* this list, in the order that they are returned by the specified
* Collection's Iterator.
*
* @param c
* elements to be inserted into this list.
*/
public synchronized boolean addAll(Collection c)
{
int numNew = c.size();
if (numNew == 0)
return false;
int len = array_.length;
Object[] newArray = new Object[len + numNew];
System.arraycopy(array_, 0, newArray, 0, len);
Iterator e = c.iterator();
for (int i = 0; i < numNew; i++)
newArray[len++] = e.next();
array_ = newArray;
return true;
}
/**
* Inserts all of the elements in the specified Collection into this list,
* starting at the specified position. Shifts the element currently at that
* position (if any) and any subsequent elements to the right (increases
* their indices). The new elements will appear in the list in the order
* that they are returned by the specified Collection's iterator.
*
* @param index
* index at which to insert first element from the specified
* collection.
* @param c
* elements to be inserted into this list.
* @exception IndexOutOfBoundsException
* index out of range (index < 0 || index > size()).
*/
public synchronized boolean addAll(int index, Collection c)
{
int len = array_.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + len);
int numNew = c.size();
if (numNew == 0)
return false;
Object[] newArray = new Object[len + numNew];
System.arraycopy(array_, 0, newArray, 0, len);
int numMoved = len - index;
if (numMoved > 0)
System.arraycopy(array_, index, newArray, index + numNew, numMoved);
Iterator e = c.iterator();
for (int i = 0; i < numNew; i++)
newArray[index++] = e.next();
array_ = newArray;
return true;
}
/**
* Check if the given index is in range. If not, throw an appropriate
* runtime exception.
* @param index
* @param length
*/
protected void rangeCheck(int index, int length)
{
if (index >= length || index < 0)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + length);
}
/**
* Save the state of the list to a stream (i.e., serialize it).
* @param s
* @throws java.io.IOException
*
* @serialData The length of the array backing the list is emitted (int),
* followed by all of its elements (each an Object) in the
* proper order.
*/
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException
{
// Write out element count, and any hidden stuff
s.defaultWriteObject();
Object[] elementData = array();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i = 0; i < elementData.length; i++)
s.writeObject(elementData[i]);
}
/**
* Reconstitute the list from a stream (i.e., deserialize it).
* @param s
* @throws java.io.IOException
* @throws ClassNotFoundException
*/
private synchronized void readObject(java.io.ObjectInputStream s) throws java.io.IOException,
ClassNotFoundException
{
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in array length and allocate array
int arrayLength = s.readInt();
Object[] elementData = new Object[arrayLength];
// Read in all elements in the proper order.
for (int i = 0; i < elementData.length; i++)
elementData[i] = s.readObject();
array_ = elementData;
}
/**
* Returns a string representation of this Collection, containing the String
* representation of each element.
*/
public String toString()
{
StringBuffer buf = new StringBuffer();
Iterator e = iterator();
buf.append("[");
int maxIndex = size() - 1;
for (int i = 0; i <= maxIndex; i++)
{
buf.append(String.valueOf(e.next()));
if (i < maxIndex)
buf.append(", ");
}
buf.append("]");
return buf.toString();
}
/**
* Compares the specified Object with this List for equality. Returns true
* if and only if the specified Object is also a List, both Lists have the
* same size, and all corresponding pairs of elements in the two Lists are
* <em>equal</em>. (Two elements <code>e1</code> and <code>e2</code>
* are <em>equal</em> if
* <code>(e1==null ? e2==null : e1.equals(e2))</code>.) In other words,
* two Lists are defined to be equal if they contain the same elements in
* the same order.
* <p>
* This implementation first checks if the specified object is this List. If
* so, it returns true; if not, it checks if the specified object is a List.
* If not, it returns false; if so, it iterates over both lists, comparing
* corresponding pairs of elements. If any comparison returns false, this
* method returns false. If either Iterator runs out of elements before
* before the other it returns false (as the Lists are of unequal length);
* otherwise it returns true when the iterations complete.
*
* @param o
* the Object to be compared for equality with this List.
* @return true if the specified Object is equal to this List.
*/
public boolean equals(Object o)
{
if (o == this)
return true;
if (!(o instanceof List))
return false;
List l2 = (List)(o);
if (size() != l2.size())
return false;
ListIterator e1 = listIterator();
ListIterator e2 = l2.listIterator();
while (e1.hasNext())
{
Object o1 = e1.next();
Object o2 = e2.next();
if (!(o1 == null ? o2 == null : o1.equals(o2)))
return false;
}
return true;
}
/**
* Returns the hash code value for this List.
* <p>
* This implementation uses exactly the code that is used to define the List
* hash function in the documentation for List.hashCode.
*/
public int hashCode()
{
int hashCode = 1;
Iterator i = iterator();
while (i.hasNext())
{
Object obj = i.next();
hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
}
return hashCode;
}
/**
* Returns an Iterator over the elements contained in this collection. The
* iterator provides a snapshot of the state of the list when the iterator
* was constructed. No synchronization is needed while traversing the
* iterator. The iterator does <em>NOT</em> support the
* <code>remove</code> method.
*/
public Iterator iterator()
{
return new COWIterator(array(), 0);
}
/**
* Returns an Iterator of the elements in this List (in proper sequence).
* The iterator provides a snapshot of the state of the list when the
* iterator was constructed. No synchronization is needed while traversing
* the iterator. The iterator does <em>NOT</em> support the
* <code>remove</code>, <code>set</code>, or <code>add</code>
* methods.
*
*/
public ListIterator listIterator()
{
return new COWIterator(array(), 0);
}
/**
* Returns a ListIterator of the elements in this List (in proper sequence),
* starting at the specified position in the List. The specified index
* indicates the first element that would be returned by an initial call to
* nextElement. An initial call to previousElement would return the element
* with the specified index minus one. The ListIterator returned by this
* implementation will throw an UnsupportedOperationException in its remove,
* set and add methods.
*
* @param index
* index of first element to be returned from the ListIterator
* (by a call to getNext).
* @exception IndexOutOfBoundsException
* index is out of range (index < 0 || index > size()).
*/
public ListIterator listIterator(final int index)
{
Object[] elementData = array();
int len = elementData.length;
if (index < 0 || index > len)
throw new IndexOutOfBoundsException("Index: " + index);
return new COWIterator(array(), index);
}
protected static class COWIterator implements ListIterator
{
/** Snapshot of the array * */
protected final Object[] array;
/**
* Index of element to be returned by subsequent call to next.
*/
protected int cursor;
protected COWIterator(Object[] elementArray, int initialCursor)
{
array = elementArray;
cursor = initialCursor;
}
public boolean hasNext()
{
return cursor < array.length;
}
public boolean hasPrevious()
{
return cursor > 0;
}
public Object next()
{
try
{
return array[cursor++];
}
catch (IndexOutOfBoundsException ex)
{
throw new NoSuchElementException();
}
}
public Object previous()
{
try
{
return array[--cursor];
}
catch (IndexOutOfBoundsException e)
{
throw new NoSuchElementException();
}
}
public int nextIndex()
{
return cursor;
}
public int previousIndex()
{
return cursor - 1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
*
* @exception UnsupportedOperationException
* remove is not supported by this Iterator.
*/
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
*
* @exception UnsupportedOperationException
* set is not supported by this Iterator.
*/
public void set(Object o)
{
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
*
* @exception UnsupportedOperationException
* add is not supported by this Iterator.
*/
public void add(Object o)
{
throw new UnsupportedOperationException();
}
}
/**
* Returns a view of the portion of this List between fromIndex, inclusive,
* and toIndex, exclusive. The returned List is backed by this List, so
* changes in the returned List are reflected in this List, and vice-versa.
* While mutative operations are supported, they are probably not very
* useful for CopyOnWriteArrays.
* </p>
* The semantics of the List returned by this method become undefined if the
* backing list (i.e., this List) is <i>structurally modified</i> in any
* way other than via the returned List. (Structural modifications are those
* that change the size of the List, or otherwise perturb it in such a
* fashion that iterations in progress may yield incorrect results.)
*
* @param fromIndex
* low endpoint (inclusive) of the subList.
* @param toKey
* high endpoint (exclusive) of the subList.
* @return a view of the specified range within this List.
* @exception IndexOutOfBoundsException
* Illegal endpoint index value (fromIndex < 0 || toIndex
* > size || fromIndex > toIndex).
*/
public synchronized List subList(int fromIndex, int toIndex)
{
// synchronized since sublist ctor depends on it.
int len = array_.length;
if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
throw new IndexOutOfBoundsException();
return new COWSubList(this, fromIndex, toIndex);
}
protected static class COWSubList extends AbstractList
{
/*
* This is currently a bit sleazy. The class extends AbstractList merely
* for convenience, to avoid having to define addAll, etc. This doesn't
* hurt, but is stupid and wasteful. This class does not need or use
* modCount mechanics in AbstractList, but does need to check for
* concurrent modification using similar mechanics. On each operation,
* the array that we expect the backing list to use is checked and
* updated. Since we do this for all of the base operations invoked by
* those defined in AbstractList, all is well.
*
* It's not clear whether this is worth cleaning up. The kinds of list
* operations inherited from AbstractList are are already so slow on COW
* sublists that adding a bit more space/time doesn't seem even
* noticeable.
*/
protected final CopyOnWriteArrayList l;
protected final int offset;
protected int size;
protected Object[] expectedArray;
protected COWSubList(CopyOnWriteArrayList list, int fromIndex, int toIndex)
{
l = list;
expectedArray = l.array();
offset = fromIndex;
size = toIndex - fromIndex;
}
// only call this holding l's lock
protected void checkForComodification()
{
if (l.array_ != expectedArray)
throw new ConcurrentModificationException();
}
// only call this holding l's lock
protected void rangeCheck(int index)
{
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: " + index + ",Size: " + size);
}
public Object set(int index, Object element)
{
synchronized (l)
{
rangeCheck(index);
checkForComodification();
Object x = l.set(index + offset, element);
expectedArray = l.array_;
return x;
}
}
public Object get(int index)
{
synchronized (l)
{
rangeCheck(index);
checkForComodification();
return l.get(index + offset);
}
}
public int size()
{
synchronized (l)
{
checkForComodification();
return size;
}
}
public void add(int index, Object element)
{
synchronized (l)
{
checkForComodification();
if (index < 0 || index > size)
throw new IndexOutOfBoundsException();
l.add(index + offset, element);
expectedArray = l.array_;
size++;
}
}
public Object remove(int index)
{
synchronized (l)
{
rangeCheck(index);
checkForComodification();
Object result = l.remove(index + offset);
expectedArray = l.array_;
size--;
return result;
}
}
public Iterator iterator()
{
synchronized (l)
{
checkForComodification();
return new COWSubListIterator(0);
}
}
public ListIterator listIterator(final int index)
{
synchronized (l)
{
checkForComodification();
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
return new COWSubListIterator(index);
}
}
protected class COWSubListIterator implements ListIterator
{
protected final ListIterator i;
protected final int index;
protected COWSubListIterator(int index)
{
this.index = index;
i = l.listIterator(index + offset);
}
public boolean hasNext()
{
return nextIndex() < size;
}
public Object next()
{
if (hasNext())
return i.next();
else
throw new NoSuchElementException();
}
public boolean hasPrevious()
{
return previousIndex() >= 0;
}
public Object previous()
{
if (hasPrevious())
return i.previous();
else
throw new NoSuchElementException();
}
public int nextIndex()
{
return i.nextIndex() - offset;
}
public int previousIndex()
{
return i.previousIndex() - offset;
}
public void remove()
{
throw new UnsupportedOperationException();
}
public void set(Object o)
{
throw new UnsupportedOperationException();
}
public void add(Object o)
{
throw new UnsupportedOperationException();
}
}
public List subList(int fromIndex, int toIndex)
{
synchronized (l)
{
checkForComodification();
if (fromIndex < 0 || toIndex > size)
throw new IndexOutOfBoundsException();
return new COWSubList(l, fromIndex + offset, toIndex + offset);
}
}
}
}
Related examples in the same category