/* * Copyright 2001-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.collections; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.ListIterator; /** * <p>A customized implementation of <code>java.util.ArrayList</code> designed * to operate in a multithreaded environment where the large majority of * method calls are read-only, instead of structural changes. When operating * in "fast" mode, read calls are non-synchronized and write calls perform the * following steps:</p> * <ul> * <li>Clone the existing collection * <li>Perform the modification on the clone * <li>Replace the existing collection with the (modified) clone * </ul> * <p>When first created, objects of this class default to "slow" mode, where * all accesses of any type are synchronized but no cloning takes place. This * is appropriate for initially populating the collection, followed by a switch * to "fast" mode (by calling <code>setFast(true)</code>) after initialization * is complete.</p> * * <p><strong>NOTE</strong>: If you are creating and accessing an * <code>ArrayList</code> only within a single thread, you should use * <code>java.util.ArrayList</code> directly (with no synchronization), for * maximum performance.</p> * * <p><strong>NOTE</strong>: <i>This class is not cross-platform. * Using it may cause unexpected failures on some architectures.</i> * It suffers from the same problems as the double-checked locking idiom. * In particular, the instruction that clones the internal collection and the * instruction that sets the internal reference to the clone can be executed * or perceived out-of-order. This means that any read operation might fail * unexpectedly, as it may be reading the state of the internal collection * before the internal collection is fully formed. * For more information on the double-checked locking idiom, see the * <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html"> * Double-Checked Locking Idiom Is Broken Declaration</a>.</p> * * @since Commons Collections 1.0 * @version $Revision: 178303 $ $Date: 2005-05-24 23:39:51 +0100 (Tue, 24 May 2005) $ * * @author Craig R. McClanahan * @author Stephen Colebourne */ public class FastArrayList extends ArrayList { // ----------------------------------------------------------- Constructors /** * Construct a an empty list. */ public FastArrayList() { super(); this.list = new ArrayList(); } /** * Construct an empty list with the specified capacity. * * @param capacity The initial capacity of the empty list */ public FastArrayList(int capacity) { super(); this.list = new ArrayList(capacity); } /** * Construct a list containing the elements of the specified collection, * in the order they are returned by the collection's iterator. * * @param collection The collection whose elements initialize the contents * of this list */ public FastArrayList(Collection collection) { super(); this.list = new ArrayList(collection); } // ----------------------------------------------------- Instance Variables /** * The underlying list we are managing. */ protected ArrayList list = null; // ------------------------------------------------------------- Properties /** * Are we operating in "fast" mode? */ protected boolean fast = false; /** * Returns true if this list is operating in fast mode. * * @return true if this list is operating in fast mode */ public boolean getFast() { return (this.fast); } /** * Sets whether this list will operate in fast mode. * * @param fast true if the list should operate in fast mode */ public void setFast(boolean fast) { this.fast = fast; } // --------------------------------------------------------- Public Methods /** * Appends the specified element to the end of this list. * * @param element The element to be appended */ public boolean add(Object element) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.add(element); list = temp; return (result); } } else { synchronized (list) { return (list.add(element)); } } } /** * Insert the specified element at the specified position in this list, * and shift all remaining elements up one position. * * @param index Index at which to insert this element * @param element The element to be inserted * * @exception IndexOutOfBoundsException if the index is out of range */ public void add(int index, Object element) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); temp.add(index, element); list = temp; } } else { synchronized (list) { list.add(index, element); } } } /** * Append 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 collection The collection to be appended */ public boolean addAll(Collection collection) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.addAll(collection); list = temp; return (result); } } else { synchronized (list) { return (list.addAll(collection)); } } } /** * Insert all of the elements in the specified Collection at the specified * position in this list, and shift any previous elements upwards as * needed. * * @param index Index at which insertion takes place * @param collection The collection to be added * * @exception IndexOutOfBoundsException if the index is out of range */ public boolean addAll(int index, Collection collection) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.addAll(index, collection); list = temp; return (result); } } else { synchronized (list) { return (list.addAll(index, collection)); } } } /** * Remove all of the elements from this list. The list will be empty * after this call returns. * * @exception UnsupportedOperationException if <code>clear()</code> * is not supported by this list */ public void clear() { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); temp.clear(); list = temp; } } else { synchronized (list) { list.clear(); } } } /** * Return a shallow copy of this <code>FastArrayList</code> instance. * The elements themselves are not copied. */ public Object clone() { FastArrayList results = null; if (fast) { results = new FastArrayList(list); } else { synchronized (list) { results = new FastArrayList(list); } } results.setFast(getFast()); return (results); } /** * Return <code>true</code> if this list contains the specified element. * * @param element The element to test for */ public boolean contains(Object element) { if (fast) { return (list.contains(element)); } else { synchronized (list) { return (list.contains(element)); } } } /** * Return <code>true</code> if this list contains all of the elements * in the specified Collection. * * @param collection Collection whose elements are to be checked */ public boolean containsAll(Collection collection) { if (fast) { return (list.containsAll(collection)); } else { synchronized (list) { return (list.containsAll(collection)); } } } /** * Increase the capacity of this <code>ArrayList</code> instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param capacity The new minimum capacity */ public void ensureCapacity(int capacity) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); temp.ensureCapacity(capacity); list = temp; } } else { synchronized (list) { list.ensureCapacity(capacity); } } } /** * Compare the specified object with this list for equality. This * implementation uses exactly the code that is used to define the * list equals function in the documentation for the * <code>List.equals</code> method. * * @param o Object to be compared to this list */ public boolean equals(Object o) { // Simple tests that require no synchronization if (o == this) return (true); else if (!(o instanceof List)) return (false); List lo = (List) o; // Compare the sets of elements for equality if (fast) { ListIterator li1 = list.listIterator(); ListIterator li2 = lo.listIterator(); while (li1.hasNext() && li2.hasNext()) { Object o1 = li1.next(); Object o2 = li2.next(); if (!(o1 == null ? o2 == null : o1.equals(o2))) return (false); } return (!(li1.hasNext() || li2.hasNext())); } else { synchronized (list) { ListIterator li1 = list.listIterator(); ListIterator li2 = lo.listIterator(); while (li1.hasNext() && li2.hasNext()) { Object o1 = li1.next(); Object o2 = li2.next(); if (!(o1 == null ? o2 == null : o1.equals(o2))) return (false); } return (!(li1.hasNext() || li2.hasNext())); } } } /** * Return the element at the specified position in the list. * * @param index The index of the element to return * * @exception IndexOutOfBoundsException if the index is out of range */ public Object get(int index) { if (fast) { return (list.get(index)); } else { synchronized (list) { return (list.get(index)); } } } /** * Return the hash code value for this list. This implementation uses * exactly the code that is used to define the list hash function in the * documentation for the <code>List.hashCode</code> method. */ public int hashCode() { if (fast) { int hashCode = 1; java.util.Iterator i = list.iterator(); while (i.hasNext()) { Object o = i.next(); hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); } return (hashCode); } else { synchronized (list) { int hashCode = 1; java.util.Iterator i = list.iterator(); while (i.hasNext()) { Object o = i.next(); hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); } return (hashCode); } } } /** * Search for the first occurrence of the given argument, testing * for equality using the <code>equals()</code> method, and return * the corresponding index, or -1 if the object is not found. * * @param element The element to search for */ public int indexOf(Object element) { if (fast) { return (list.indexOf(element)); } else { synchronized (list) { return (list.indexOf(element)); } } } /** * Test if this list has no elements. */ public boolean isEmpty() { if (fast) { return (list.isEmpty()); } else { synchronized (list) { return (list.isEmpty()); } } } /** * Return an iterator over the elements in this list in proper sequence. * <p> * <b>Thread safety</b><br /> * The iterator returned is thread-safe ONLY in FAST mode. * In slow mode there is no way to synchronize, or make the iterator thread-safe. * <p> * In fast mode iteration and modification may occur in parallel on different threads, * however there is a restriction. Modification must be EITHER via the Iterator * interface methods OR the List interface. If a mixture of modification * methods is used a ConcurrentModificationException is thrown from the iterator * modification method. If the List modification methods are used the changes are * NOT visible in the iterator (it shows the list contents at the time the iterator * was created). * * @return the iterator */ public Iterator iterator() { if (fast) { return new ListIter(0); } else { return list.iterator(); } } /** * Search for the last occurrence of the given argument, testing * for equality using the <code>equals()</code> method, and return * the corresponding index, or -1 if the object is not found. * * @param element The element to search for */ public int lastIndexOf(Object element) { if (fast) { return (list.lastIndexOf(element)); } else { synchronized (list) { return (list.lastIndexOf(element)); } } } /** * Return an iterator of the elements of this list, in proper sequence. * <p> * <b>Thread safety</b><br /> * The iterator returned is thread-safe ONLY in FAST mode. * In slow mode there is no way to synchronize, or make the iterator thread-safe. * <p> * In fast mode iteration and modification may occur in parallel on different threads, * however there is a restriction. Modification must be EITHER via the Iterator * interface methods OR the List interface. If a mixture of modification * methods is used a ConcurrentModificationException is thrown from the iterator * modification method. If the List modification methods are used the changes are * NOT visible in the iterator (it shows the list contents at the time the iterator * was created). * * @return the list iterator */ public ListIterator listIterator() { if (fast) { return new ListIter(0); } else { return list.listIterator(); } } /** * Return an iterator of the elements of this list, in proper sequence, * starting at the specified position. * <p> * <b>Thread safety</b><br /> * The iterator returned is thread-safe ONLY in FAST mode. * In slow mode there is no way to synchronize, or make the iterator thread-safe. * <p> * In fast mode iteration and modification may occur in parallel on different threads, * however there is a restriction. Modification must be EITHER via the Iterator * interface methods OR the List interface. If a mixture of modification * methods is used a ConcurrentModificationException is thrown from the iterator * modification method. If the List modification methods are used the changes are * NOT visible in the iterator (it shows the list contents at the time the iterator * was created). * * @param index The starting position of the iterator to return * @return the list iterator * @exception IndexOutOfBoundsException if the index is out of range */ public ListIterator listIterator(int index) { if (fast) { return new ListIter(index); } else { return list.listIterator(index); } } /** * Remove the element at the specified position in the list, and shift * any subsequent elements down one position. * * @param index Index of the element to be removed * * @exception IndexOutOfBoundsException if the index is out of range */ public Object remove(int index) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); Object result = temp.remove(index); list = temp; return (result); } } else { synchronized (list) { return (list.remove(index)); } } } /** * Remove the first occurrence of the specified element from the list, * and shift any subsequent elements down one position. * * @param element Element to be removed */ public boolean remove(Object element) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.remove(element); list = temp; return (result); } } else { synchronized (list) { return (list.remove(element)); } } } /** * Remove from this collection all of its elements that are contained * in the specified collection. * * @param collection Collection containing elements to be removed * * @exception UnsupportedOperationException if this optional operation * is not supported by this list */ public boolean removeAll(Collection collection) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.removeAll(collection); list = temp; return (result); } } else { synchronized (list) { return (list.removeAll(collection)); } } } /** * Remove from this collection all of its elements except those that are * contained in the specified collection. * * @param collection Collection containing elements to be retained * * @exception UnsupportedOperationException if this optional operation * is not supported by this list */ public boolean retainAll(Collection collection) { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); boolean result = temp.retainAll(collection); list = temp; return (result); } } else { synchronized (list) { return (list.retainAll(collection)); } } } /** * Replace the element at the specified position in this list with * the specified element. Returns the previous object at that position. * <br><br> * <strong>IMPLEMENTATION NOTE</strong> - This operation is specifically * documented to not be a structural change, so it is safe to be performed * without cloning. * * @param index Index of the element to replace * @param element The new element to be stored * * @exception IndexOutOfBoundsException if the index is out of range */ public Object set(int index, Object element) { if (fast) { return (list.set(index, element)); } else { synchronized (list) { return (list.set(index, element)); } } } /** * Return the number of elements in this list. */ public int size() { if (fast) { return (list.size()); } else { synchronized (list) { return (list.size()); } } } /** * Return a view of the portion of this list between fromIndex * (inclusive) and toIndex (exclusive). The returned list is backed * by this list, so non-structural changes in the returned list are * reflected in this list. The returned list supports * all of the optional list operations supported by this list. * * @param fromIndex The starting index of the sublist view * @param toIndex The index after the end of the sublist view * * @exception IndexOutOfBoundsException if an index is out of range */ public List subList(int fromIndex, int toIndex) { if (fast) { return new SubList(fromIndex, toIndex); } else { return list.subList(fromIndex, toIndex); } } /** * Return an array containing all of the elements in this list in the * correct order. */ public Object[] toArray() { if (fast) { return (list.toArray()); } else { synchronized (list) { return (list.toArray()); } } } /** * Return 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. * * @param array Array defining the element type of the returned list * * @exception ArrayStoreException if the runtime type of <code>array</code> * is not a supertype of the runtime type of every element in this list */ public Object[] toArray(Object array[]) { if (fast) { return (list.toArray(array)); } else { synchronized (list) { return (list.toArray(array)); } } } /** * Return a String representation of this object. */ public String toString() { StringBuffer sb = new StringBuffer("FastArrayList["); sb.append(list.toString()); sb.append("]"); return (sb.toString()); } /** * Trim the capacity of this <code>ArrayList</code> instance to be the * list's current size. An application can use this operation to minimize * the storage of an <code>ArrayList</code> instance. */ public void trimToSize() { if (fast) { synchronized (this) { ArrayList temp = (ArrayList) list.clone(); temp.trimToSize(); list = temp; } } else { synchronized (list) { list.trimToSize(); } } } private class SubList implements List { private int first; private int last; private List expected; public SubList(int first, int last) { this.first = first; this.last = last; this.expected = list; } private List get(List l) { if (list != expected) { throw new ConcurrentModificationException(); } return l.subList(first, last); } public void clear() { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); get(temp).clear(); last = first; list = temp; expected = temp; } } else { synchronized (list) { get(expected).clear(); } } } public boolean remove(Object o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); boolean r = get(temp).remove(o); if (r) last--; list = temp; expected = temp; return r; } } else { synchronized (list) { return get(expected).remove(o); } } } public boolean removeAll(Collection o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); List sub = get(temp); boolean r = sub.removeAll(o); if (r) last = first + sub.size(); list = temp; expected = temp; return r; } } else { synchronized (list) { return get(expected).removeAll(o); } } } public boolean retainAll(Collection o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); List sub = get(temp); boolean r = sub.retainAll(o); if (r) last = first + sub.size(); list = temp; expected = temp; return r; } } else { synchronized (list) { return get(expected).retainAll(o); } } } public int size() { if (fast) { return get(expected).size(); } else { synchronized (list) { return get(expected).size(); } } } public boolean isEmpty() { if (fast) { return get(expected).isEmpty(); } else { synchronized (list) { return get(expected).isEmpty(); } } } public boolean contains(Object o) { if (fast) { return get(expected).contains(o); } else { synchronized (list) { return get(expected).contains(o); } } } public boolean containsAll(Collection o) { if (fast) { return get(expected).containsAll(o); } else { synchronized (list) { return get(expected).containsAll(o); } } } public Object[] toArray(Object[] o) { if (fast) { return get(expected).toArray(o); } else { synchronized (list) { return get(expected).toArray(o); } } } public Object[] toArray() { if (fast) { return get(expected).toArray(); } else { synchronized (list) { return get(expected).toArray(); } } } public boolean equals(Object o) { if (o == this) return true; if (fast) { return get(expected).equals(o); } else { synchronized (list) { return get(expected).equals(o); } } } public int hashCode() { if (fast) { return get(expected).hashCode(); } else { synchronized (list) { return get(expected).hashCode(); } } } public boolean add(Object o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); boolean r = get(temp).add(o); if (r) last++; list = temp; expected = temp; return r; } } else { synchronized (list) { return get(expected).add(o); } } } public boolean addAll(Collection o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); boolean r = get(temp).addAll(o); if (r) last += o.size(); list = temp; expected = temp; return r; } } else { synchronized (list) { return get(expected).addAll(o); } } } public void add(int i, Object o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); get(temp).add(i, o); last++; list = temp; expected = temp; } } else { synchronized (list) { get(expected).add(i, o); } } } public boolean addAll(int i, Collection o) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); boolean r = get(temp).addAll(i, o); list = temp; if (r) last += o.size(); expected = temp; return r; } } else { synchronized (list) { return get(expected).addAll(i, o); } } } public Object remove(int i) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); Object o = get(temp).remove(i); last--; list = temp; expected = temp; return o; } } else { synchronized (list) { return get(expected).remove(i); } } } public Object set(int i, Object a) { if (fast) { synchronized (FastArrayList.this) { ArrayList temp = (ArrayList) list.clone(); Object o = get(temp).set(i, a); list = temp; expected = temp; return o; } } else { synchronized (list) { return get(expected).set(i, a); } } } public Iterator iterator() { return new SubListIter(0); } public ListIterator listIterator() { return new SubListIter(0); } public ListIterator listIterator(int i) { return new SubListIter(i); } public Object get(int i) { if (fast) { return get(expected).get(i); } else { synchronized (list) { return get(expected).get(i); } } } public int indexOf(Object o) { if (fast) { return get(expected).indexOf(o); } else { synchronized (list) { return get(expected).indexOf(o); } } } public int lastIndexOf(Object o) { if (fast) { return get(expected).lastIndexOf(o); } else { synchronized (list) { return get(expected).lastIndexOf(o); } } } public List subList(int f, int l) { if (list != expected) { throw new ConcurrentModificationException(); } return new SubList(first + f, f + l); } private class SubListIter implements ListIterator { private List expected; private ListIterator iter; private int lastReturnedIndex = -1; public SubListIter(int i) { this.expected = list; this.iter = SubList.this.get(expected).listIterator(i); } private void checkMod() { if (list != expected) { throw new ConcurrentModificationException(); } } List get() { return SubList.this.get(expected); } public boolean hasNext() { checkMod(); return iter.hasNext(); } public Object next() { checkMod(); lastReturnedIndex = iter.nextIndex(); return iter.next(); } public boolean hasPrevious() { checkMod(); return iter.hasPrevious(); } public Object previous() { checkMod(); lastReturnedIndex = iter.previousIndex(); return iter.previous(); } public int previousIndex() { checkMod(); return iter.previousIndex(); } public int nextIndex() { checkMod(); return iter.nextIndex(); } public void remove() { checkMod(); if (lastReturnedIndex < 0) { throw new IllegalStateException(); } get().remove(lastReturnedIndex); last--; expected = list; iter = get().listIterator(lastReturnedIndex); lastReturnedIndex = -1; } public void set(Object o) { checkMod(); if (lastReturnedIndex < 0) { throw new IllegalStateException(); } get().set(lastReturnedIndex, o); expected = list; iter = get().listIterator(previousIndex() + 1); } public void add(Object o) { checkMod(); int i = nextIndex(); get().add(i, o); last++; expected = list; iter = get().listIterator(i + 1); lastReturnedIndex = -1; } } } private class ListIter implements ListIterator { private List expected; private ListIterator iter; private int lastReturnedIndex = -1; public ListIter(int i) { this.expected = list; this.iter = get().listIterator(i); } private void checkMod() { if (list != expected) { throw new ConcurrentModificationException(); } } List get() { return expected; } public boolean hasNext() { return iter.hasNext(); } public Object next() { lastReturnedIndex = iter.nextIndex(); return iter.next(); } public boolean hasPrevious() { return iter.hasPrevious(); } public Object previous() { lastReturnedIndex = iter.previousIndex(); return iter.previous(); } public int previousIndex() { return iter.previousIndex(); } public int nextIndex() { return iter.nextIndex(); } public void remove() { checkMod(); if (lastReturnedIndex < 0) { throw new IllegalStateException(); } get().remove(lastReturnedIndex); expected = list; iter = get().listIterator(lastReturnedIndex); lastReturnedIndex = -1; } public void set(Object o) { checkMod(); if (lastReturnedIndex < 0) { throw new IllegalStateException(); } get().set(lastReturnedIndex, o); expected = list; iter = get().listIterator(previousIndex() + 1); } public void add(Object o) { checkMod(); int i = nextIndex(); get().add(i, o); expected = list; iter = get().listIterator(i + 1); lastReturnedIndex = -1; } } }