remove
, add
etc..) operation
* is performed.O(n)
or worse,
* but traversal operations are fast and efficient, especially when running in
* a multi-thread environment without the need to design complex synchronize
* mechanisms.Iterator
s in this class work on a snapshot of the backing store
* at the moment the iterator itself was created, hence the iterator will not
* reflect changes in the underlying storage. Thus, update operation on the
* Iterator
s are not supported, but as interferences from other
* threads are impossible, no ConcurrentModificationException
* will be ever thrown from within the Iterator
.
*
*
* CopyOnWriteArrayList listeners =
* new CopyOnWriteArrayList();
*
* [...]
*
* for (final EventListener listener : listeners)
* {
* Runnable dispatcher = new Runnable() {
* public void run()
* {
* listener.preferenceChange(event);
* }
* };
*
* Executor executor = Executors.newSingleThreadExecutor();
* executor.execute(dispatcher);
* }
*
*
* @since 1.5
*/
public class CopyOnWriteArrayListindex
at which
* e
appears in this List, or -1 if it does not
* appear.
*
* @param e the element whose inclusion in the list is being tested
* @param index the index at which the search begins
* @return the index where e
was found
*/
public int indexOf(E e, int index)
{
E[] data = this.data;
for (int i = index; i < data.length; i++)
if (equals(e, data[i]))
return i;
return -1;
}
/**
* Returns the highest index at which element appears in this List, or -1 if
* it does not appear.
*
* @param e
* the element whose inclusion in the List is being tested
* @return the index where e was found
*/
public int lastIndexOf(Object e)
{
E[] data = this.data;
for (int i = data.length - 1; i >= 0; i--)
if (equals(e, data[i]))
return i;
return -1;
}
/**
* Returns the highest index lesser equal index
at
* which e
appears in this List, or -1 if it does not
* appear.
*
* @param e the element whose inclusion in the list is being tested
* @param index the index at which the search begins
* @return the index where e
was found
*/
public int lastIndexOf(E e, int index)
{
E[] data = this.data;
for (int i = index; i >= 0; i--)
if (equals(e, data[i]))
return i;
return -1;
}
/**
* Creates a shallow copy of this ArrayList (elements are not cloned).
*
* @return the cloned object
*/
public Object clone()
{
CopyOnWriteArrayListtrue
if the object was removed, false
* otherwise.
*
* @param element the object to be removed.
* @return true if element was removed, false otherwise. false means also that
* the underlying storage was unchanged after this operation concluded.
*/
public synchronized boolean remove(Object element)
{
E[] snapshot = this.data;
int len = snapshot.length;
if (len == 0)
return false;
E[] newData = (E[]) new Object[len - 1];
// search the element to remove while filling the backup array
// this way we can run this method in O(n)
int elementIndex = -1;
for (int i = 0; i < snapshot.length; i++)
{
if (equals(element, snapshot[i]))
{
elementIndex = i;
break;
}
if (i < newData.length)
newData[i] = snapshot[i];
}
if (elementIndex < 0)
return false;
System.arraycopy(snapshot, elementIndex + 1, newData, elementIndex,
snapshot.length - elementIndex - 1);
this.data = newData;
return true;
}
/**
* Removes all the elements contained in the given collection.
* This method removes the elements that are contained in both
* this list and in the given collection.
*
* @param c the collection containing the elements to be removed from this
* list.
* @return true if at least one element was removed, indicating that
* the list internal storage changed as a result, false otherwise.
*/
public synchronized boolean removeAll(Collection> c)
{
if (c.size() == 0)
return false;
E [] snapshot = this.data;
E [] storage = (E[]) new Object[this.data.length];
boolean changed = false;
int length = 0;
for (E element : snapshot)
{
// copy all the elements, including null values
// if the collection can hold it
// FIXME: slow operation
if (c.contains(element))
changed = true;
else
storage[length++] = element;
}
if (!changed)
return false;
E[] newData = (E[]) new Object[length];
System.arraycopy(storage, 0, newData, 0, length);
this.data = newData;
return true;
}
/**
* Removes all the elements that are not in the passed collection.
* If the collection is void, this method has the same effect of
* clear()
.
* Please, note that this method is extremely slow (unless the argument has
* size == 0
) and has bad performance is both space and time
* usage.
*
* @param c the collection containing the elements to be retained by this
* list.
* @return true the list internal storage changed as a result of this
* operation, false otherwise.
*/
public synchronized boolean retainAll(Collection> c)
{
// if the given collection does not contain elements
// we remove all the elements from our storage
if (c.size() == 0)
{
this.clear();
return true;
}
E [] snapshot = this.data;
E [] storage = (E[]) new Object[this.data.length];
int length = 0;
for (E element : snapshot)
{
if (c.contains(element))
storage[length++] = element;
}
// means we retained all the elements previously in our storage
// we are running already slow here, but at least we avoid copying
// another array and changing the internal storage
if (length == snapshot.length)
return false;
E[] newData = (E[]) new Object[length];
System.arraycopy(storage, 0, newData, 0, length);
this.data = newData;
return true;
}
/**
* Removes all elements from this List
*/
public synchronized void clear()
{
data = (E[]) new Object[0];
}
/**
* Add each element in the supplied Collection to this List. It is undefined
* what happens if you modify the list while this is taking place; for
* example, if the collection contains this list. c can contain objects of any
* type, as well as null values.
*
* @param c
* a Collection containing elements to be added to this List
* @return true if the list was modified, in other words c is not empty
* @throws NullPointerException
* if c is null
*/
public synchronized boolean addAll(Collection< ? extends E> c)
{
return addAll(data.length, c);
}
/**
* Add all elements in the supplied collection, inserting them beginning at
* the specified index. c can contain objects of any type, as well as null
* values.
*
* @param index
* the index at which the elements will be inserted
* @param c
* the Collection containing the elements to be inserted
* @throws IndexOutOfBoundsException
* if index < 0 || index > 0
* @throws NullPointerException
* if c is null
*/
public synchronized boolean addAll(int index, Collection< ? extends E> c)
{
if (index < 0 || index > this.size())
throw new IndexOutOfBoundsException("index = " + index);
int csize = c.size();
if (csize == 0)
return false;
E[] data = this.data;
Iterator extends E> itr = c.iterator();
E[] newData = (E[]) new Object[data.length + csize];
// avoid this call at all if we were asked to put the elements at the
// beginning of our storage
if (index != 0)
System.arraycopy(data, 0, newData, 0, index);
int itemsLeft = index;
for (E value : c)
newData[index++] = value;
// now copy the remaining elements
System.arraycopy(data, itemsLeft, newData, 0, data.length - itemsLeft);
this.data = newData;
return true;
}
/**
* Adds an element if the list does not contains it already.
*
* @param val the element to add to the list.
* @return true if the element was added, false otherwise.
*/
public synchronized boolean addIfAbsent(E val)
{
if (contains(val))
return false;
add(val);
return true;
}
/**
* Adds all the element from the given collection that are not already
* in this list.
*
* @param c the Collection containing the elements to be inserted
* @return true the list internal storage changed as a result of this
* operation, false otherwise.
*/
public synchronized int addAllAbsent(Collection extends E> c)
{
int size = c.size();
if (size == 0)
return 0;
E [] snapshot = this.data;
E [] storage = (E[]) new Object[size];
size = 0;
for (E val : c)
{
if (!this.contains(val))
storage[size++] = val;
}
if (size == 0)
return 0;
// append storage to data
E [] newData = (E[]) new Object[snapshot.length + size];
System.arraycopy(snapshot, 0, newData, 0, snapshot.length);
System.arraycopy(storage, 0, newData, snapshot.length, size);
this.data = newData;
return size;
}
public String toString()
{
return Arrays.toString(this.data);
}
public boolean equals(Object o)
{
if (o == null)
return false;
if (this == o)
return true;
// let's see if 'o' is a list, if so, we need to compare the elements
// as returned by the iterator
if (o instanceof List)
{
List> source = (List>) o;
if (source.size() != this.size())
return false;
Iterator> sourceIterator = source.iterator();
for (E element : this)
{
if (!element.equals(sourceIterator.next()))
return false;
}
return true;
}
return false;
}
public int hashCode()
{
// see http://java.sun.com/6/docs/api/java/util/List.html#hashcode()
int hashcode = 1;
for (E element : this)
{
hashcode = 31 * hashcode + (element == null ? 0 : element.hashCode());
}
return hashcode;
}
/**
* Return an Iterator containing the elements of this list.
* The Iterator uses a snapshot of the state of the internal storage
* at the moment this method is called and does not support
* update operations, so no synchronization is needed to traverse the
* iterator.
*
* @return an Iterator containing the elements of this list in sequence.
*/
public Iterator* * This implementation returns a subclass of AbstractList. It stores, in * private fields, the offset and size of the sublist, and the expected * modCount of the backing list. If the backing list implements RandomAccess, * the sublist will also. *
*
* The subclass's set(int, Object)
, get(int)
,
* add(int, Object)
, remove(int)
,
* addAll(int, Collection)
and
* removeRange(int, int)
methods all delegate to the
* corresponding methods on the backing abstract list, after
* bounds-checking the index and adjusting for the offset. The
* addAll(Collection c)
method merely returns addAll(size, c).
* The listIterator(int)
method returns a "wrapper object"
* over a list iterator on the backing list, which is created with the
* corresponding method on the backing list. The iterator()
* method merely returns listIterator(), and the size()
method
* merely returns the subclass's size field.
*
*
* All methods first check to see if the actual modCount of the backing
* list is equal to its expected value, and throw a
* ConcurrentModificationException if it is not.
*
* @param fromIndex the index that the returned list should start from
* (inclusive)
* @param toIndex the index that the returned list should go to (exclusive)
* @return a List backed by a subsection of this list
* @throws IndexOutOfBoundsException if fromIndex < 0
* || toIndex > size()
* @throws IndexOutOfBoundsException if fromIndex > toIndex
* @see ConcurrentModificationException
* @see RandomAccess
*/
public synchronized Listnext()
*
* @return The index of the next element.
*/
public int nextIndex()
{
return i.nextIndex() - offset;
}
/**
* Returns the index of the previous element in the
* list, which will be retrieved by previous()
*
* @return The index of the previous element.
*/
public int previousIndex()
{
return i.previousIndex() - offset;
}
/**
* Removes the last object retrieved by next()
* from the list, if the list supports object removal.
*
* @throws IllegalStateException if the iterator is positioned
* before the start of the list or the last object has already
* been removed.
* @throws UnsupportedOperationException if the list does
* not support removing elements.
*/
public void remove()
{
throw new UnsupportedOperationException("Modification not supported " +
"on CopyOnWriteArrayList iterators");
}
/**
* Replaces the last object retrieved by next()
* or previous
with o, if the list supports object
* replacement and an add or remove operation has not already
* been performed.
*
* @throws IllegalStateException if the iterator is positioned
* before the start of the list or the last object has already
* been removed.
* @throws UnsupportedOperationException if the list doesn't support
* the addition or removal of elements.
* @throws ClassCastException if the type of o is not a valid type
* for this list.
* @throws IllegalArgumentException if something else related to o
* prevents its addition.
* @throws ConcurrentModificationException if the list
* has been modified elsewhere.
*/
public void set(E o)
{
throw new UnsupportedOperationException("Modification not supported " +
"on CopyOnWriteArrayList iterators");
}
/**
* Adds the supplied object before the element that would be returned
* by a call to next()
, if the list supports addition.
*
* @param o The object to add to the list.
* @throws UnsupportedOperationException if the list doesn't support
* the addition of new elements.
* @throws ClassCastException if the type of o is not a valid type
* for this list.
* @throws IllegalArgumentException if something else related to o
* prevents its addition.
* @throws ConcurrentModificationException if the list
* has been modified elsewhere.
*/
public void add(E o)
{
throw new UnsupportedOperationException("Modification not supported " +
"on CopyOnWriteArrayList iterators");
}
};
}
} // class SubList
/**
* This class is a RandomAccess version of SubList, as required by
* {@link AbstractList#subList(int, int)}.
*
* @author Eric Blake (ebb9@email.byu.edu)
*/
private static final class RandomAccessSubList