A thread-safe 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.
It is more efficient than alternatives i.e. Vector and synchronizedList(java.util. Collections.synchronizedList()) when traversal operations large number mutations.
It is useful when we don't want to synchronize traversals, yet need to prevent the 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 ConcurrentModificationException.
The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
Element-changing operations on iterators themselves remove, set, and add are not supported. These methods throw UnsupportedOperationException.
private static class COWSubListIterator<E> implements ListIterator<E> {
private final ListIterator<E> it;
private final int offset;
private final int size;
COWSubListIterator(List<E> l, int index, int offset, int size) {
this.offset = offset;
this.size = size;
it= l.listIterator(index+offset);
}
public boolean hasNext() {
return nextIndex() < size;
}
public E next() {
if (hasNext())
return it.next();
else
throw new NoSuchElementException();
}
public boolean hasPrevious() {
return previousIndex() >= 0;
}
public E previous() {
if (hasPrevious())
return it.previous();
else
throw new NoSuchElementException();
}
public int nextIndex() {
return it.nextIndex() - offset;
}
public int previousIndex() {
return it.previousIndex() - offset;
}
public void remove() {
throw new UnsupportedOperationException();
}
public void set(E e) {
throw new UnsupportedOperationException();
}
public void add(E e) {
throw new UnsupportedOperationException();
}
}
All elements are permitted, including null.
Unlike ConcurrentHashMap, write operations that write or access multiple elements in the list (such as addAll(), retainAll()) will be atomic. During a write operation, the array must be locked completely against other writes.
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
/** 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.
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class) {
elements = ((CopyOnWriteArrayList<?>)c).getArray();
} else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class) {
elements=Arrays.copyOf(elements, elements.length,Object[].class);
}
}
setArray(elements);
}
/**
* Creates a list holding a copy of the given array.
* @throws NullPointerException if the specified array is null
*/
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
/**
* Returns a shallow copy of this list.
* @return a clone of this list
*/
public Object clone() {
try {
@SuppressWarnings("unchecked")
CopyOnWriteArrayList<E> clone =
(CopyOnWriteArrayList<E>) super.clone();
clone.resetLock();
return clone;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
public E get(int index) {
return get(getArray(), index);
}
// Positional Access Operations
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* Replaces the element at the specified position in this list
* with the specified element.
* @throws IndexOutOfBoundsException
*/
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
/**
* Appends the specified element to the end of this list.
* @param e element to be appended to this list
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
/**
* 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
*/
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
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 = Arrays.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);
} finally {
lock.unlock();
}
}
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left
* Returns the element that was removed from the list.
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.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;
} finally {
lock.unlock();
}
}
/**
* 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.
* Returns true if this list contained the specified element.
* @param o element to be removed from this list, if present
* @return true if this list contained the specified element
*/
public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
/**
* Removes from this list all of the elements whose index is between
* fromIndex, inclusive, and toIndex, exclusive.
* @throws IndexOutOfBoundsException if fromIndex or toIndex out of range
* fromIndex < 0 || toIndex > size() || toIndex < fromIndex
*/
void removeRange(int fromIndex, int toIndex) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int newlen = len - (toIndex - fromIndex);
int numMoved = len - toIndex;
if (numMoved == 0)
setArray(Arrays.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);
}
} finally {
lock.unlock();
}
}
package com.collection;
ReplyDeleteimport java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Java program to demonstrate What is CopyOnWriteArrayList in Java,
* Iterator of CopyOnWriteArrayList doesn’t support add, remove or any modification operation.
*/
public class CopyOnWriteArrayListExample {
public static void main(String args[]) {
CopyOnWriteArrayList threadSafeList = new CopyOnWriteArrayList();
threadSafeList.add("Java");
threadSafeList.add("J2EE");
threadSafeList.add("Collection");
//add, remove operator is not supported by CopyOnWriteArrayList iterator
Iterator failSafeIterator = threadSafeList.iterator();
while(failSafeIterator.hasNext()) {
System.out.printf("Read from CopyOnWriteArrayList : %s %n", failSafeIterator.next());
threadSafeList.add("Sameer");
// not supported in CopyOnWriteArrayList in Java
failSafeIterator.remove();
}
}
}
output :
Read from CopyOnWriteArrayList : Exception in thread "main" Java
java.lang.UnsupportedOperationException
at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1004)
at com.collection.CopyOnWriteArrayListExample.main(CopyOnWriteArrayListExample.java:27)
CopyOnWriteArray List iterator all update method throw UnsupportedOperationException
Inner class for Iterator
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 E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; remove
* is not supported by this iterator.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; set
* is not supported by this iterator.
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; add
* is not supported by this iterator.
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
}