CopyOnWriteArrayList.java Source code

Java tutorial

Introduction

Here is the source code for CopyOnWriteArrayList.java

Source

/*
 * 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 &lt; 0 || index &gt;=
     *                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 &lt; 0 || index &gt;= 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 &lt; 0 || index &gt; 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 &lt; 0 || index &gt;= 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 &lt; 0 ||
     *                fromIndex &gt;= size() || toIndex &gt; size() || toIndex
     *                &lt; 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 &lt; 0 || index &gt; 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 &lt; 0 || index &gt; 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 &lt; 0 || toIndex
     *                &gt; size || fromIndex &gt; 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);
            }
        }

    }

}