Copy On Write ArrayList : ArrayList « Collections Data Structure « Java






Copy On Write ArrayList

     

/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group.  Adapted and released, under explicit permission,
 * from JDK 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.
 */

import java.util.AbstractList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;


/**
 * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
 * operations (<tt>add</tt>, <tt>set</tt>, and so on) are implemented by
 * making a fresh copy of the underlying array.
 *
 * <p> This is ordinarily too costly, but may be <em>more</em> efficient
 * than alternatives when traversal operations vastly outnumber
 * mutations, and is useful when you cannot or don't want to
 * synchronize traversals, yet need to preclude interference among
 * concurrent threads.  The "snapshot" style 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 and the iterator is
 * guaranteed not to throw <tt>ConcurrentModificationException</tt>.
 * The iterator will not reflect additions, removals, or changes to
 * the list since the iterator was created.  Element-changing
 * operations on iterators themselves (<tt>remove</tt>, <tt>set</tt>, and
 * <tt>add</tt>) are not supported. These methods throw
 * <tt>UnsupportedOperationException</tt>.
 *
 * <p>All elements are permitted, including <tt>null</tt>.
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../guide/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @since 1.5
 * @author Doug Lea
 */
public class CopyOnWriteArrayList
    implements List, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    /** The array, accessed only via getArray/setArray. */
    private volatile transient Object[] array;

    /**
     * This has been made public to support more efficient iteration.
     * <strong>DO NOT MODIFY this array upon getting it</strong>.
     * Otherwise you risk wreaking havoc on your list. In fact, if you are
     * not the author of this comment, you probably shouldn't use it at all.
     * @return this lists internal array
     */
    public Object[]  getArray()    { return array; }
    
    void      setArray(Object[] a) { array = a; }

    /**
     * Creates an empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    /**
     * Creates a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection of initially held elements
     * @throws NullPointerException if the specified collection is null
     */
    public CopyOnWriteArrayList(Collection c) {
        Object[] elements = new Object[c.size()];
        int size = 0;
        for (Iterator itr = c.iterator(); itr.hasNext(); ) {
            Object e = itr.next();
            elements[size++] = e;
        }
        setArray(elements);
    }

    /**
     * Creates a list holding a copy of the given array.
     *
     * @param toCopyIn the array (a copy of this array is used as the
     *        internal array)
     * @throws NullPointerException if the specified array is null
     */
    public CopyOnWriteArrayList(Object[] toCopyIn) {
        copyIn(toCopyIn, 0, toCopyIn.length);
    }

    /**
     * Replaces the held array with a copy of the <tt>n</tt> elements
     * of the provided array, starting at position <tt>first</tt>.  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.
     */
    private void copyIn(Object[] toCopyIn, int first, int n) {
        int limit = first + n;
        if (limit > toCopyIn.length)
            throw new IndexOutOfBoundsException();
        Object[] newElements = copyOfRange(toCopyIn, first, limit,
                                          Object[].class);
        synchronized (this) { setArray(newElements); }
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return the number of elements in this list
     */
    public int size() {
        return getArray().length;
    }

    /**
     * Returns <tt>true</tt> if this list contains no elements.
     *
     * @return <tt>true</tt> if this list contains no elements
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * Test for equality, coping with nulls.
     */
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null ? o2 == null : o1.equals(o2));
    }

    /**
     * static version of indexOf, to allow repeated calls without
     * needing to re-acquire array each time.
     * @param o element to search for
     * @param elements the array
     * @param index first index to search
     * @param fence one past last index to search
     * @return index of element, or -1 if absent
     */
    private static int indexOf(Object o, Object[] elements,
                               int index, int fence) {
        if (o == null) {
            for (int i = index; i < fence; i++)
                if (elements[i] == null)
                    return i;
        } else {
            for (int i = index; i < fence; i++)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     * static version of lastIndexOf.
     * @param o element to search for
     * @param elements the array
     * @param index first index to search
     * @return index of element, or -1 if absent
     */
    private static int lastIndexOf(Object o, Object[] elements, int index) {
        if (o == null) {
            for (int i = index; i >= 0; i--)
                if (elements[i] == null)
                    return i;
        } else {
            for (int i = index; i >= 0; i--)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     * Returns <tt>true</tt> if this list contains the specified element.
     * More formally, returns <tt>true</tt> if and only if this list contains
     * at least one element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this list is to be tested
     * @return <tt>true</tt> if this list contains the specified element
     */
    public boolean contains(Object o) {
        Object[] elements = getArray();
        return indexOf(o, elements, 0, elements.length) >= 0;
    }

    /**
     * {@inheritDoc}
     */
    public int indexOf(Object o) {
        Object[] elements = getArray();
        return indexOf(o, elements, 0, elements.length);
    }


    /**
     * Returns the index of the first occurrence of the specified element in
     * this list, searching forwards from <tt>index</tt>, or returns -1 if
     * the element is not found.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(i&nbsp;&gt;=&nbsp;index&nbsp;&amp;&amp;&nbsp;(e==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;e.equals(get(i))))</tt>,
     * or -1 if there is no such index.
     *
     * @param e element to search for
     * @param index index to start searching from
     * @return the index of the first occurrence of the element in
     *         this list at position <tt>index</tt> or later in the list;
     *         <tt>-1</tt> if the element is not found.
     * @throws IndexOutOfBoundsException if the specified index is negative
     */
    public int indexOf(Object e, int index) {
        Object[] elements = getArray();
        return indexOf(e, elements, index, elements.length);
    }

    /**
     * {@inheritDoc}
     */
    public int lastIndexOf(Object o) {
        Object[] elements = getArray();
        return lastIndexOf(o, elements, elements.length - 1);
    }

    /**
     * Returns the index of the last occurrence of the specified element in
     * this list, searching backwards from <tt>index</tt>, or returns -1 if
     * the element is not found.
     * More formally, returns the highest index <tt>i</tt> such that
     * <tt>(i&nbsp;&lt;=&nbsp;index&nbsp;&amp;&amp;&nbsp;(e==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;e.equals(get(i))))</tt>,
     * or -1 if there is no such index.
     *
     * @param e element to search for
     * @param index index to start searching backwards from
     * @return the index of the last occurrence of the element at position
     *         less than or equal to <tt>index</tt> in this list;
     *         -1 if the element is not found.
     * @throws IndexOutOfBoundsException if the specified index is greater
     *         than or equal to the current size of this list
     */
    public int lastIndexOf(Object e, int index) {
        Object[] elements = getArray();
        return lastIndexOf(e, elements, index);
    }

    /**
     * Returns a shallow copy of this list.  (The elements themselves
     * are not copied.)
     *
     * @return a clone of this list
     */
    public Object clone() {
        try {
            return super.clone();
        } 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 proper sequence (from first to last element).
     *
     * <p>The returned array will be "safe" in that no references to it are
     * maintained by this list.  (In other words, this method must allocate
     * a new array).  The caller is thus free to modify the returned array.
     *
     * <p>This method acts as bridge between array-based and collection-based
     * APIs.
     *
     * @return an array containing all the elements in this list
     */
    public Object[] toArray() {
        Object[] elements = getArray();
        return copyOf(elements, elements.length);
    }

    /**
     * Returns an array containing all of the elements in this list in
     * proper sequence (from first to last element); 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 this list fits in the specified array with room to spare
     * (i.e., the array has more elements than this list), the element in
     * the array immediately following the end of the list is set to
     * <tt>null</tt>.  (This is useful in determining the length of this
     * list <i>only</i> if the caller knows that this list does not contain
     * any null elements.)
     *
     * <p>Like the {@link #toArray()} method, this method acts as bridge between
     * array-based and collection-based APIs.  Further, this method allows
     * precise control over the runtime type of the output array, and may,
     * under certain circumstances, be used to save allocation costs.
     *
     * <p>Suppose <tt>x</tt> is a list known to contain only strings.
     * The following code can be used to dump the list into a newly
     * allocated array of <tt>String</tt>:
     *
     * <pre>
     *     String[] y = x.toArray(new String[0]);</pre>
     *
     * Note that <tt>toArray(new Object[0])</tt> is identical in function to
     * <tt>toArray()</tt>.
     *
     * @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 all the elements in this list
     * @throws ArrayStoreException if the runtime type of the specified array
     *         is not a supertype of the runtime type of every element in
     *         this list
     * @throws NullPointerException if the specified array is null
     */
    public Object[] toArray(Object a[]) {
        Object[] elements = getArray();
        int len = elements.length;
        if (a.length < len)
            return copyOf(elements, len, a.getClass());
        else {
            System.arraycopy(elements, 0, a, 0, len);
            if (a.length > len)
                a[len] = null;
            return a;
        }
    }

    // Positional Access Operations

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public Object get(int index) {
        return (getArray()[index]);
    }

    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public synchronized Object set(int index, Object element) {
        Object[] elements = getArray();
        int len = elements.length;
        Object oldValue = elements[index];

        if (oldValue != element) {
            Object[] newElements = copyOf(elements, len);
            newElements[index] = element;
            setArray(newElements);
        }
        return oldValue;
    }

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as per the spec for {@link Collection#add})
     */
    public boolean add(Object e) {
        synchronized (this) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
        }
        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).
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public synchronized void add(int index, Object element) {
        Object[] elements = getArray();
        int len = elements.length;
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: " + index+
                                                ", Size: " + len);
        Object[] newElements;
        int numMoved = len - index;
        if (numMoved == 0)
            newElements = copyOf(elements, len + 1);
        else {
            newElements = new Object[len + 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index, newElements, index + 1,
                             numMoved);
        }
        newElements[index] = element;
        setArray(newElements);
    }

    /**
     * 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.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public synchronized Object remove(int index) {
        Object[] elements = getArray();
        int len = elements.length;
        Object oldValue = elements[index];
        int numMoved = len - index - 1;
        if (numMoved == 0)
            setArray(copyOf(elements, len - 1));
        else {
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    }

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If this list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public synchronized boolean remove(Object o) {
        Object[] elements = getArray();
        int len = elements.length;
        if (len != 0) {
            // Copy while searching for element to remove
            // This wins in the normal case of element being present
            int newlen = len - 1;
            Object[] newElements = new Object[newlen];

            for (int i = 0; i < newlen; ++i) {
                if (eq(o, elements[i])) {
                    // found one;  copy remaining and exit
                    for (int k = i + 1; k < len; ++k)
                        newElements[k-1] = elements[k];
                    setArray(newElements);
                    return true;
                } else
                    newElements[i] = elements[i];
            }

            // special handling for last cell
            if (eq(o, elements[newlen])) {
                setArray(newElements);
                return true;
            }
        }
        return false;
    }

    /**
     * Removes from this list all of the elements whose index is between
     * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.
     * Shifts any succeeding elements to the left (reduces their index).
     * This call shortens the list by <tt>(toIndex - fromIndex)</tt> elements.
     * (If <tt>toIndex==fromIndex</tt>, this operation has no effect.)
     *
     * @param fromIndex index of first element to be removed
     * @param toIndex index after last element to be removed
     * @throws IndexOutOfBoundsException if fromIndex or toIndex out of
     *              range (fromIndex &lt; 0 || fromIndex &gt;= size() || toIndex
     *              &gt; size() || toIndex &lt; fromIndex)
     */
    private synchronized void removeRange(int fromIndex, int toIndex) {
        Object[] elements = getArray();
        int len = elements.length;

        if (fromIndex < 0 || fromIndex >= len ||
            toIndex > len || toIndex < fromIndex)
            throw new IndexOutOfBoundsException();
        int newlen = len - (toIndex - fromIndex);
        int numMoved = len - toIndex;
        if (numMoved == 0)
            setArray(copyOf(elements, newlen));
        else {
            Object[] newElements = new Object[newlen];
            System.arraycopy(elements, 0, newElements, 0, fromIndex);
            System.arraycopy(elements, toIndex, newElements,
                             fromIndex, numMoved);
            setArray(newElements);
        }
    }

    /**
     * Append the element if not present.
     *
     * @param e element to be added to this list, if absent
     * @return <tt>true</tt> if the element was added
     */
    public synchronized boolean addIfAbsent(Object e) {
        // Copy while checking if already present.
        // This wins in the most common case where it is not present
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = new Object[len + 1];
        for (int i = 0; i < len; ++i) {
            if (eq(e, elements[i]))
                return false; // exit, throwing away copy
            else
                newElements[i] = elements[i];
        }
        newElements[len] = e;
        setArray(newElements);
        return true;
    }

    /**
     * Returns <tt>true</tt> if this list contains all of the elements of the
     * specified collection.
     *
     * @param c collection to be checked for containment in this list
     * @return <tt>true</tt> if this list contains all of the elements of the
     *         specified collection
     * @throws NullPointerException if the specified collection is null
     * @see #contains(Object)
     */
    public boolean containsAll(Collection c) {
        Object[] elements = getArray();
        int len = elements.length;
        for (Iterator itr = c.iterator(); itr.hasNext(); ) {
            Object e = itr.next();
            if (indexOf(e, elements, 0, len) < 0)
                return false;
        }
        return true;
    }

    /**
     * Removes from this list 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.
     *
     * @param c collection containing elements to be removed from this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws ClassCastException if the class of an element of this list
     *         is incompatible with the specified collection (optional)
     * @throws NullPointerException if this list contains a null element and the
     *         specified collection does not permit null elements (optional),
     *         or if the specified collection is null
     * @see #remove(Object)
     */
    public synchronized boolean removeAll(Collection c) {
        Object[] elements = getArray();
        int len = elements.length;
        if (len != 0) {
            // temp array holds those elements we know we want to keep
            int newlen = 0;
            Object[] temp = new Object[len];
            for (int i = 0; i < len; ++i) {
                Object element = elements[i];
                if (!c.contains(element))
                    temp[newlen++] = element;
            }
            if (newlen != len) {
                setArray(copyOfRange(temp, 0, newlen, Object[].class));
                return true;
            }
        }
        return false;
    }

    /**
     * Retains only the elements in this list that are contained in the
     * specified collection.  In other words, removes from this list all of
     * its elements that are not contained in the specified collection.
     *
     * @param c collection containing elements to be retained in this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws ClassCastException if the class of an element of this list
     *         is incompatible with the specified collection (optional)
     * @throws NullPointerException if this list contains a null element and the
     *         specified collection does not permit null elements (optional),
     *         or if the specified collection is null
     * @see #remove(Object)
     */
    public synchronized boolean retainAll(Collection c) {
        Object[] elements = getArray();
        int len = elements.length;
        if (len != 0) {
            // temp array holds those elements we know we want to keep
            int newlen = 0;
            Object[] temp = new Object[len];
            for (int i = 0; i < len; ++i) {
                Object element = elements[i];
                if (c.contains(element))
                    temp[newlen++] = element;
            }
            if (newlen != len) {
                setArray(copyOfRange(temp, 0, newlen, Object[].class));
                return true;
            }
        }
        return false;
    }

    /**
     * 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 collection containing elements to be added to this list
     * @return the number of elements added
     * @throws NullPointerException if the specified collection is null
     * @see #addIfAbsent(Object)
     */
    public int addAllAbsent(Collection c) {
        int numNew = c.size();
        if (numNew == 0)
            return 0;
        synchronized (this) {
            Object[] elements = getArray();
            int len = elements.length;

            Object[] temp = new Object[numNew];
            int added = 0;
            for (Iterator itr = c.iterator(); itr.hasNext(); ) {
                Object e = itr.next();
                if (indexOf(e, elements, 0, len) < 0 &&
                    indexOf(e, temp, 0, added) < 0)
                    temp[added++] = e;
            }
            if (added != 0) {
                Object[] newElements = new Object[len + added];
                System.arraycopy(elements, 0, newElements, 0, len);
                System.arraycopy(temp, 0, newElements, len, added);
                setArray(newElements);
            }
            return added;
        }
    }

    /**
     * Removes all of the elements from this list.
     * The list will be empty after this call returns.
     */
    public synchronized void clear() {
        setArray(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 collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     * @see #add(Object)
     */
    public boolean addAll(Collection c) {
        int numNew = c.size();
        if (numNew == 0)
            return false;
        synchronized (this) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = new Object[len + numNew];
            System.arraycopy(elements, 0, newElements, 0, len);
            for (Iterator itr = c.iterator(); itr.hasNext(); ) {
                Object e = itr.next();
                newElements[len++] = e;
            }
            setArray(newElements);
            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 this list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element
     *        from the specified collection
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     * @see #add(int,Object)
     */
    public boolean addAll(int index, Collection c) {
        int numNew = c.size();
        synchronized (this) {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: " + index +
                                                    ", Size: "+ len);
            if (numNew == 0)
                return false;
            int numMoved = len - index;
            Object[] newElements;
            if (numMoved == 0)
                newElements = copyOf(elements, len + numNew);
            else {
                newElements = new Object[len + numNew];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index,
                                 newElements, index + numNew,
                                 numMoved);
            }
            for (Iterator itr = c.iterator(); itr.hasNext(); ) {
                Object e = itr.next();
                newElements[index++] = e;
            }
            setArray(newElements);
            return true;
        }
    }

    /**
     * Save the state of the list to a stream (i.e., serialize it).
     *
     * @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.
     * @param s the stream
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{

        // Write out element count, and any hidden stuff
        s.defaultWriteObject();

        Object[] elements = getArray();
        int len = elements.length;
        // Write out array length
        s.writeInt(len);

        // Write out all elements in the proper order.
        for (int i = 0; i < len; i++)
            s.writeObject(elements[i]);
    }

    /**
     * Reconstitute the list from a stream (i.e., deserialize it).
     * @param s the stream
     */
    private 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 len = s.readInt();
        Object[] elements = new Object[len];

        // Read in all elements in the proper order.
        for (int i = 0; i < len; i++)
            elements[i] = s.readObject();
        setArray(elements);

    }

    /**
     * Returns a string representation of this list, containing
     * the String representation of each element.
     */
    public String toString() {
        Object[] elements = getArray();
        int maxIndex = elements.length - 1;
        StringBuffer buf = new StringBuffer();
        buf.append("[");
        for (int i = 0; i <= maxIndex; i++) {
            buf.append(String.valueOf(elements[i]));
            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 {@link
     * List}, both lists have the same size, and all corresponding pairs
     * of elements in the two lists are <em>equal</em>.  (Two elements
     * <tt>e1</tt> and <tt>e2</tt> are <em>equal</em> if <tt>(e1==null ?
     * e2==null : e1.equals(e2))</tt>.)  In other words, two lists are
     * defined to be equal if they contain the same elements in the same
     * order.
     *
     * @param o the object to be compared for equality with this list
     * @return <tt>true</tt> 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()) {
            if (!eq(e1.next(), e2.next()))
                return false;
        }
        return true;
    }

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

    /**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned 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
     * <tt>remove</tt> method.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator iterator() {
        return new COWIterator(getArray(), 0);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The returned 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
     * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
     */
    public ListIterator listIterator() {
        return new COWIterator(getArray(), 0);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The list iterator returned by this implementation will throw an
     * <tt>UnsupportedOperationException</tt> in its <tt>remove</tt>,
     * <tt>set</tt> and <tt>add</tt> methods.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public ListIterator listIterator(final int index) {
        Object[] elements = getArray();
        int len = elements.length;
        if (index < 0 || index > len)
            throw new IndexOutOfBoundsException("Index: " + index);

        return new COWIterator(getArray(), index);
    }

    private static class COWIterator implements ListIterator {
        /** Snapshot of the array **/
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

        public boolean hasNext() {
            return cursor < snapshot.length;
        }

        public boolean hasPrevious() {
            return cursor > 0;
        }

        public Object next() {
            try {
                return (snapshot[cursor++]);
            } catch (IndexOutOfBoundsException ex) {
                throw new NoSuchElementException();
            }
        }

        public Object previous() {
            try {
                return (snapshot[--cursor]);
            } catch (IndexOutOfBoundsException e) {
                throw new NoSuchElementException();
            }
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor - 1;
        }

        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; <tt>remove</tt>
         *         is not supported by this iterator.
         */
        public void remove() {
            throw new UnsupportedOperationException();
        }

        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; <tt>set</tt>
         *         is not supported by this iterator.
         */
        public void set(Object e) {
            throw new UnsupportedOperationException();
        }

        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; <tt>add</tt>
         *         is not supported by this iterator.
         */
        public void add(Object e) {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * Returns a view of the portion of this list between
     * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, 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 CopyOnWriteArrayLists.
     *
     * <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 toIndex high endpoint (exclusive) of the subList
     * @return a view of the specified range within this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public synchronized List subList(int fromIndex, int toIndex) {
        Object[] elements = getArray();
        int len = elements.length;
        if (fromIndex < 0 || toIndex > len  || fromIndex > toIndex)
            throw new IndexOutOfBoundsException();
        return new COWSubList(this, fromIndex, toIndex);
    }


    /**
     * Sublist for CopyOnWriteArrayList.
     * This class extends AbstractList merely for convenience, to
     * avoid having to define addAll, etc. This doesn't hurt, but
     * is 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.  While inefficient, this is not worth
     * improving.  The kinds of list operations inherited from
     * AbstractList are already so slow on COW sublists that
     * adding a bit more space/time doesn't seem even noticeable.
     */
    private static class COWSubList extends AbstractList {
        private final CopyOnWriteArrayList l;
        private final int offset;
        private int size;
        private Object[] expectedArray;

        // only call this holding l's lock
        private COWSubList(CopyOnWriteArrayList list,
                           int fromIndex, int toIndex) {
            l = list;
            expectedArray = l.getArray();
            offset = fromIndex;
            size = toIndex - fromIndex;
        }

        // only call this holding l's lock
        private void checkForComodification() {
            if (l.getArray() != expectedArray)
                throw new ConcurrentModificationException();
        }

        // only call this holding l's lock
        private 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.getArray();
                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.getArray();
                size++;
            }
        }

        public void clear() {
            synchronized (l) {
                checkForComodification();
                l.removeRange(offset, offset+size);
                expectedArray = l.getArray();
                size = 0;
            }
        }

        public Object remove(int index) {
            synchronized (l) {
                rangeCheck(index);
                checkForComodification();
                Object result = l.remove(index + offset);
                expectedArray = l.getArray();
                size--;
                return result;
            }
        }

        public Iterator iterator() {
            synchronized (l) {
                checkForComodification();
                return new COWSubListIterator(l, 0, offset, size);
            }
        }

        public ListIterator listIterator(final int index) {
            synchronized (l) {
                checkForComodification();
                if (index<0 || index>size)
                    throw new IndexOutOfBoundsException("Index: "+index+
                                                        ", Size: "+size);
                return new COWSubListIterator(l, index, offset, size);
            }
        }

        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);
            }
        }

    }


    private static class COWSubListIterator implements ListIterator {
        private final ListIterator i;
        private final int offset;
        private final int size;
        private COWSubListIterator(List l, int index, int offset,
                                   int size) {
            this.offset = offset;
            this.size = size;
            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 e) {
            throw new UnsupportedOperationException();
        }

        public void add(Object e) {
            throw new UnsupportedOperationException();
        }
    }

//    // Support for resetting lock while deserializing
//    private static final Unsafe unsafe =  Unsafe.getUnsafe();
//    private static final long lockOffset;
//    static {
//        try {
//            lockOffset = unsafe.objectFieldOffset
//                (CopyOnWriteArrayList.class.getDeclaredField("lock"));
//            } catch (Exception ex) { throw new Error(ex); }
//    }
//    private void resetLock() {
//        unsafe.putObjectVolatile(this, lockOffset, new ReentrantLock());
//    }
//

    // Temporary emulations of anticipated new j.u.Arrays functions

    private static Object[] copyOfRange(Object[] original, int from, int to,
                                        Class newType) {
        int newLength = to - from;
        if (newLength < 0)
            throw new IllegalArgumentException(from + " > " + to);
        Object[] copy = (Object[]) java.lang.reflect.Array.newInstance
            (newType.getComponentType(), newLength);
        System.arraycopy(original, from, copy, 0,
                         Math.min(original.length - from, newLength));
        return copy;
    }

    private static Object[] copyOf(Object[] original, int newLength,
                                   Class newType) {
        Object[] copy = (Object[]) java.lang.reflect.Array.newInstance
            (newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

    private static Object[] copyOf(Object[] original, int newLength) {
        return copyOf(original, newLength, original.getClass());
    }
}

   
    
    
    
    
  








Related examples in the same category

1.Get the size of an arraylist after and before add and remove methods
2.Use the Iterator returned from ArrayList to loop through an array list
3.Get generic Iterator from generic ArrayList
4.Convert an ArrayList into an array.
5.Pre-generics example that uses a collection.
6.shows the modern, generic form of collection classes
7.Use set method to change the value in an array list
8.Store user-defined objects in arraylist
9.pass the actual object you want removed.
10.Convert a List (ArrayList) to an Array with zero length array
11.Convert a List (ArrayList) to an Array with full length array
12.Remove duplicate items from an ArrayList
13.Looping through a Collection object: while loop, iterator, and for each
14.Append all elements of other Collection to Java ArrayList
15.Copy all elements of Java ArrayList to an Object Array
16.Get Size of Java ArrayList and loop through elements
17.Add an element to specified index of Java ArrayList
18.Get Sub List of Java ArrayList
19.Insert all elements of other Collection to Specified Index of Java ArrayList
20.Iterate through elements Java ArrayList using Iterator
21.Iterate through elements Java ArrayList using ListIterator
22.Remove all elements from Java ArrayList
23.Remove an element from specified index of Java ArrayList
24.Search an element of Java ArrayList
25.Get element in an ArrayList by index
26.Replace an element at specified index of Java ArrayList
27.Sort elements of Java ArrayList
28.Copy Elements of ArrayList to Java Vector
29.Copy Elements of One Java ArrayList to Another Java ArrayList
30.Find maximum element of Java ArrayList
31.Find Minimum element of Java ArrayList
32.Get Enumeration over Java ArrayList
33.Get Synchronized List from Java ArrayList
34.Perform Binary Search on Java ArrayList
35.Replace All Elements Of Java ArrayList
36.Replace all occurrences of specified element of Java ArrayList
37.Reverse order of all elements of Java ArrayList
38.Shuffle elements of Java ArrayList
39.Swap elements of Java ArrayList
40.Iterate through a Collection using Java Iterator
41.Sort Java ArrayList in descending order using comparator
42.Remove an element from Collection using Java Iterator
43.Replace an element from ArrayList using Java ListIterator
44.How to Convert an ArrayList into an array
45.Sort items of an ArrayList
46.Rotate elements of a collection
47.Search collection element
48.If an ArrayList contains a given item
49.Convert Collection to ArrayList?
50.A boolean is being stored and then retrieved from an ArrayList
51.Traverse through ArrayList in reverse direction using Java ListIterator
52.Traverse through ArrayList in forward direction using Java ListIterator
53.Remove an element from ArrayList using Java ListIterator
54.Get Previous and next index using Java ListIterator
55.A Map implemented with ArrayLists
56.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
57.This program demonstrates the ArrayList class
58.ArrayList with sorted contents.