```
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* 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.
*/
packagejava.util.concurrent;
importjava.util.AbstractList;
importjava.util.Arrays;
importjava.util.Collection;
importjava.util.Comparator;
importjava.util.ConcurrentModificationException;
importjava.util.Iterator;
importjava.util.List;
importjava.util.ListIterator;
importjava.util.NoSuchElementException;
importjava.util.Objects;
importjava.util.RandomAccess;
importjava.util.Spliterator;
importjava.util.Spliterators;
importjava.util.concurrent.locks.ReentrantLock;
importjava.util.function.Consumer;
importjava.util.function.Predicate;
importjava.util.function.UnaryOperator;
/**
* A thread-safe variant of {@linkjava.util.ArrayList} in which all mutative
* operations ({@codeadd}, {@codeset}, and so on) are implemented by
* making a fresh copy of the underlying array.
*
*
This is ordinarily too costly, but may bemoreefficient
* 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 {@codeConcurrentModificationException}.
* The iterator will not reflect additions, removals, or changes to
* the list since the iterator was created. Element-changing
* operations on iterators themselves ({@coderemove}, {@codeset}, and
* {@codeadd}) are not supported. These methods throw
* {@codeUnsupportedOperationException}.
*
*
All elements are permitted, including {@codenull}.
*
*
Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
* {@codeCopyOnWriteArrayList}
* actions subsequent to the access or removal of that element from
* the {@codeCopyOnWriteArrayList} in another thread.
*
*
This class is a member of the
*
* Java Collections Framework.
*
*@since1.5
*@authorDoug Lea
*@paramthe type of elements held in this collection
*/
public classCopyOnWriteArrayList
implementsList,RandomAccess,Cloneable,java.io.Serializable {
private static final longserialVersionUID=8673264195747942595L;
/** The lock protecting all mutators */
final transientReentrantLocklock=newReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatileObject[]array;
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
finalObject[]getArray() {
returnarray;
}
/**
* Sets the array.
*/
final voidsetArray(Object[] a) {
array= a;
}
/**
* Creates an empty list.
*/
publicCopyOnWriteArrayList() {
setArray(newObject[0]);
}
/**
* Creates a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
*@paramcthe collection of initially held elements
*@throwsNullPointerException if the specified collection is null
*/
publicCopyOnWriteArrayList(Collection 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.
*
*@paramtoCopyInthe array (a copy of this array is used as the
* internal array)
*@throwsNullPointerException if the specified array is null
*/
publicCopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn,toCopyIn.length,Object[].class));
}
/**
* Returns the number of elements in this list.
*
*@returnthe number of elements in this list
*/
public intsize() {
returngetArray().length;
}
/**
* Returns {@codetrue} if this list contains no elements.
*
*@return{@codetrue} if this list contains no elements
*/
public booleanisEmpty() {
returnsize() ==0;
}
/**
* Tests for equality, coping with nulls.
*/
private static booleaneq(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.
*@paramoelement to search for
*@paramelementsthe array
*@paramindexfirst index to search
*@paramfenceone past last index to search
*@returnindex of element, or -1 if absent
*/
private static intindexOf(Object o,Object[] elements,
intindex, intfence) {
if(o ==null) {
for(inti = index;i < fence;i++)
if(elements[i] ==null)
returni;
}else{
for(inti = index;i < fence;i++)
if(o.equals(elements[i]))
returni;
}
return-1;
}
/**
* static version of lastIndexOf.
*@paramoelement to search for
*@paramelementsthe array
*@paramindexfirst index to search
*@returnindex of element, or -1 if absent
*/
private static intlastIndexOf(Object o,Object[] elements, intindex) {
if(o ==null) {
for(inti = index;i >=0;i--)
if(elements[i] ==null)
returni;
}else{
for(inti = index;i >=0;i--)
if(o.equals(elements[i]))
returni;
}
return-1;
}
/**
* Returns {@codetrue} if this list contains the specified element.
* More formally, returns {@codetrue} if and only if this list contains
* at least one element {@codee} such that
*(o==null ? e==null : o.equals(e)).
*
*@paramoelement whose presence in this list is to be tested
*@return{@codetrue} if this list contains the specified element
*/
public booleancontains(Object o) {
Object[] elements = getArray();
returnindexOf(o,elements,0,elements.length) >=0;
}
/**
* {@inheritDoc}
*/
public intindexOf(Object o) {
Object[] elements = getArray();
returnindexOf(o,elements,0,elements.length);
}
/**
* Returns the index of the first occurrence of the specified element in
* this list, searching forwards from {@codeindex}, or returns -1 if
* the element is not found.
* More formally, returns the lowest index {@codei} such that
*(i >= index && (e==null ? get(i)==null : e.equals(get(i)))),
* or -1 if there is no such index.
*
*@parameelement to search for
*@paramindexindex to start searching from
*@returnthe index of the first occurrence of the element in
* this list at position {@codeindex} or later in the list;
* {@code-1} if the element is not found.
*@throwsIndexOutOfBoundsException if the specified index is negative
*/
public intindexOf(Ee, intindex) {
Object[] elements = getArray();
returnindexOf(e,elements,index,elements.length);
}
/**
* {@inheritDoc}
*/
public intlastIndexOf(Object o) {
Object[] elements = getArray();
returnlastIndexOf(o,elements,elements.length-1);
}
/**
* Returns the index of the last occurrence of the specified element in
* this list, searching backwards from {@codeindex}, or returns -1 if
* the element is not found.
* More formally, returns the highest index {@codei} such that
*(i <= index && (e==null ? get(i)==null : e.equals(get(i)))),
* or -1 if there is no such index.
*
*@parameelement to search for
*@paramindexindex to start searching backwards from
*@returnthe index of the last occurrence of the element at position
* less than or equal to {@codeindex} in this list;
* -1 if the element is not found.
*@throwsIndexOutOfBoundsException if the specified index is greater
* than or equal to the current size of this list
*/
public intlastIndexOf(Ee, intindex) {
Object[] elements = getArray();
returnlastIndexOf(e,elements,index);
}
/**
* Returns a shallow copy of this list. (The elements themselves
* are not copied.)
*
*@returna clone of this list
*/
publicObjectclone() {
try{
@SuppressWarnings("unchecked")
CopyOnWriteArrayList clone =
(CopyOnWriteArrayList)super.clone();
clone.resetLock();
returnclone;
}catch(CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw newInternalError();
}
}
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
*
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.
*
*
This method acts as bridge between array-based and collection-based
* APIs.
*
*@returnan array containing all the elements in this list
*/
publicObject[]toArray() {
Object[] elements = getArray();
returnArrays.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.
*
*
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
* {@codenull}. (This is useful in determining the length of this
* listonlyif the caller knows that this list does not contain
* any null elements.)
*
*
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.
*
*
Suppose {@codex} is a list known to contain only strings.
* The following code can be used to dump the list into a newly
* allocated array of {@codeString}:
*
*
{@codeString[] y = x.toArray(new String[0]);}
*
* Note that {@codetoArray(new Object[0])} is identical in function to
* {@codetoArray()}.
*
*@paramathe 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.
*@returnan array containing all the elements in this list
*@throwsArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this list
*@throwsNullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
publicT[]toArray(Ta[]) {
Object[] elements = getArray();
intlen = elements.length;
if(a.length< len)
return(T[]) Arrays.copyOf(elements,len,a.getClass());
else{
System.arraycopy(elements,0,a,0,len);
if(a.length> len)
a[len] =null;
returna;
}
}
// Positional Access Operations
@SuppressWarnings("unchecked")
privateEget(Object[] a, intindex) {
return(E) a[index];
}
/**
* {@inheritDoc}
*
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
publicEget(intindex) {
returnget(getArray(),index);
}
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
publicEset(intindex,Eelement) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
EoldValue = get(elements,index);
if(oldValue != element) {
intlen = 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);
}
returnoldValue;
}finally{
lock.unlock();
}
}
/**
* Appends the specified element to the end of this list.
*
*@parameelement to be appended to this list
*@return{@codetrue} (as specified by {@linkCollection#add})
*/
public booleanadd(Ee) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = 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).
*
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
public voidadd(intindex,Eelement) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(index > len || index <0)
throw newIndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
intnumMoved = len - index;
if(numMoved ==0)
newElements = Arrays.copyOf(elements,len +1);
else{
newElements =newObject[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 (subtracts one from their
* indices). Returns the element that was removed from the list.
*
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
publicEremove(intindex) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
EoldValue = get(elements,index);
intnumMoved = len - index -1;
if(numMoved ==0)
setArray(Arrays.copyOf(elements,len -1));
else{
Object[] newElements =newObject[len -1];
System.arraycopy(elements,0,newElements,0,index);
System.arraycopy(elements,index +1,newElements,index,
numMoved);
setArray(newElements);
}
returnoldValue;
}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. More formally, removes the element with the lowest index
* {@codei} such that
*(o==null ? get(i)==null : o.equals(get(i)))
* (if such an element exists). Returns {@codetrue} if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
*@paramoelement to be removed from this list, if present
*@return{@codetrue} if this list contained the specified element
*/
public booleanremove(Object o) {
Object[] snapshot = getArray();
intindex =indexOf(o,snapshot,0,snapshot.length);
return(index <0) ?false: remove(o,snapshot,index);
}
/**
* A version of remove(Object) using the strong hint that given
* recent snapshot contains o at the given index.
*/
private booleanremove(Object o,Object[] snapshot, intindex) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] current = getArray();
intlen = current.length;
if(snapshot != current) findIndex: {
intprefix = Math.min(index,len);
for(inti =0;i < prefix;i++) {
if(current[i] != snapshot[i] &&eq(o,current[i])) {
index = i;
breakfindIndex;
}
}
if(index >= len)
return false;
if(current[index] == o)
breakfindIndex;
index =indexOf(o,current,index,len);
if(index <0)
return false;
}
Object[] newElements =newObject[len -1];
System.arraycopy(current,0,newElements,0,index);
System.arraycopy(current,index +1,
newElements,index,
len - index -1);
setArray(newElements);
return true;
}finally{
lock.unlock();
}
}
/**
* Removes from this list all of the elements whose index is between
* {@codefromIndex}, inclusive, and {@codetoIndex}, exclusive.
* Shifts any succeeding elements to the left (reduces their index).
* This call shortens the list by {@code(toIndex - fromIndex)} elements.
* (If {@codetoIndex==fromIndex}, this operation has no effect.)
*
*@paramfromIndexindex of first element to be removed
*@paramtoIndexindex after last element to be removed
*@throwsIndexOutOfBoundsException if fromIndex or toIndex out of range
* ({@codefromIndex < 0 || toIndex > size() || toIndex < fromIndex})
*/
voidremoveRange(intfromIndex, inttoIndex) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(fromIndex <0|| toIndex > len || toIndex < fromIndex)
throw newIndexOutOfBoundsException();
intnewlen = len - (toIndex - fromIndex);
intnumMoved = len - toIndex;
if(numMoved ==0)
setArray(Arrays.copyOf(elements,newlen));
else{
Object[] newElements =newObject[newlen];
System.arraycopy(elements,0,newElements,0,fromIndex);
System.arraycopy(elements,toIndex,newElements,
fromIndex,numMoved);
setArray(newElements);
}
}finally{
lock.unlock();
}
}
/**
* Appends the element, if not present.
*
*@parameelement to be added to this list, if absent
*@return{@codetrue} if the element was added
*/
public booleanaddIfAbsent(Ee) {
Object[] snapshot = getArray();
returnindexOf(e,snapshot,0,snapshot.length) >=0?false:
addIfAbsent(e,snapshot);
}
/**
* A version of addIfAbsent using the strong hint that given
* recent snapshot does not contain e.
*/
private booleanaddIfAbsent(Ee,Object[] snapshot) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] current = getArray();
intlen = current.length;
if(snapshot != current) {
// Optimize for lost race to another addXXX operation
intcommon = Math.min(snapshot.length,len);
for(inti =0;i < common;i++)
if(current[i] != snapshot[i] &&eq(e,current[i]))
return false;
if(indexOf(e,current,common,len) >=0)
return false;
}
Object[] newElements = Arrays.copyOf(current,len +1);
newElements[len] = e;
setArray(newElements);
return true;
}finally{
lock.unlock();
}
}
/**
* Returns {@codetrue} if this list contains all of the elements of the
* specified collection.
*
*@paramccollection to be checked for containment in this list
*@return{@codetrue} if this list contains all of the elements of the
* specified collection
*@throwsNullPointerException if the specified collection is null
*@see#contains(Object)
*/
public booleancontainsAll(Collection c) {
Object[] elements = getArray();
intlen = elements.length;
for(Object e : c) {
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.
*
*@paramccollection containing elements to be removed from this list
*@return{@codetrue} if this list changed as a result of the call
*@throwsClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (optional)
*@throwsNullPointerException 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 booleanremoveAll(Collection c) {
if(c ==null)throw newNullPointerException();
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(len !=0) {
// temp array holds those elements we know we want to keep
intnewlen =0;
Object[] temp =newObject[len];
for(inti =0;i < len;++i) {
Object element = elements[i];
if(!c.contains(element))
temp[newlen++] = element;
}
if(newlen != len) {
setArray(Arrays.copyOf(temp,newlen));
return true;
}
}
return false;
}finally{
lock.unlock();
}
}
/**
* 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.
*
*@paramccollection containing elements to be retained in this list
*@return{@codetrue} if this list changed as a result of the call
*@throwsClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (optional)
*@throwsNullPointerException 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 booleanretainAll(Collection c) {
if(c ==null)throw newNullPointerException();
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(len !=0) {
// temp array holds those elements we know we want to keep
intnewlen =0;
Object[] temp =newObject[len];
for(inti =0;i < len;++i) {
Object element = elements[i];
if(c.contains(element))
temp[newlen++] = element;
}
if(newlen != len) {
setArray(Arrays.copyOf(temp,newlen));
return true;
}
}
return false;
}finally{
lock.unlock();
}
}
/**
* 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.
*
*@paramccollection containing elements to be added to this list
*@returnthe number of elements added
*@throwsNullPointerException if the specified collection is null
*@see#addIfAbsent(Object)
*/
public intaddAllAbsent(Collection c) {
Object[] cs = c.toArray();
if(cs.length==0)
return0;
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
intadded =0;
// uniquify and compact elements in cs
for(inti =0;i < cs.length;++i) {
Object e = cs[i];
if(indexOf(e,elements,0,len) <0&&
indexOf(e,cs,0,added) <0)
cs[added++] = e;
}
if(added >0) {
Object[] newElements = Arrays.copyOf(elements,len + added);
System.arraycopy(cs,0,newElements,len,added);
setArray(newElements);
}
returnadded;
}finally{
lock.unlock();
}
}
/**
* Removes all of the elements from this list.
* The list will be empty after this call returns.
*/
public voidclear() {
finalReentrantLock lock =this.lock;
lock.lock();
try{
setArray(newObject[0]);
}finally{
lock.unlock();
}
}
/**
* 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.
*
*@paramccollection containing elements to be added to this list
*@return{@codetrue} if this list changed as a result of the call
*@throwsNullPointerException if the specified collection is null
*@see#add(Object)
*/
public booleanaddAll(Collection c) {
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList)c).getArray() : c.toArray();
if(cs.length==0)
return false;
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(len ==0&& cs.getClass() == Object[].class)
setArray(cs);
else{
Object[] newElements = Arrays.copyOf(elements,len + cs.length);
System.arraycopy(cs,0,newElements,len,cs.length);
setArray(newElements);
}
return true;
}finally{
lock.unlock();
}
}
/**
* 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.
*
*@paramindexindex at which to insert the first element
* from the specified collection
*@paramccollection containing elements to be added to this list
*@return{@codetrue} if this list changed as a result of the call
*@throwsIndexOutOfBoundsException {@inheritDoc}
*@throwsNullPointerException if the specified collection is null
*@see#add(int,Object)
*/
public booleanaddAll(intindex,Collection c) {
Object[] cs = c.toArray();
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(index > len || index <0)
throw newIndexOutOfBoundsException("Index: "+index+
", Size: "+len);
if(cs.length==0)
return false;
intnumMoved = len - index;
Object[] newElements;
if(numMoved ==0)
newElements = Arrays.copyOf(elements,len + cs.length);
else{
newElements =newObject[len + cs.length];
System.arraycopy(elements,0,newElements,0,index);
System.arraycopy(elements,index,
newElements,index + cs.length,
numMoved);
}
System.arraycopy(cs,0,newElements,index,cs.length);
setArray(newElements);
return true;
}finally{
lock.unlock();
}
}
public voidforEach(Consumer action) {
if(action ==null)throw newNullPointerException();
Object[] elements = getArray();
intlen = elements.length;
for(inti =0;i < len;++i) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
action.accept(e);
}
}
public booleanremoveIf(Predicate filter) {
if(filter ==null)throw newNullPointerException();
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(len !=0) {
intnewlen =0;
Object[] temp =newObject[len];
for(inti =0;i < len;++i) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
if(!filter.test(e))
temp[newlen++] = e;
}
if(newlen != len) {
setArray(Arrays.copyOf(temp,newlen));
return true;
}
}
return false;
}finally{
lock.unlock();
}
}
public voidreplaceAll(UnaryOperator operator) {
if(operator ==null)throw newNullPointerException();
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
Object[] newElements = Arrays.copyOf(elements,len);
for(inti =0;i < len;++i) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
newElements[i] = operator.apply(e);
}
setArray(newElements);
}finally{
lock.unlock();
}
}
public voidsort(Comparator c) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
Object[] newElements = Arrays.copyOf(elements,elements.length);
@SuppressWarnings("unchecked")E[] es = (E[])newElements;
Arrays.sort(es,c);
setArray(newElements);
}finally{
lock.unlock();
}
}
/**
* Saves this list to a stream (that is, serializes it).
*
*@paramsthe stream
*@throwsjava.io.IOException if an I/O error occurs
*@serialDataThe length of the array backing the list is emitted
* (int), followed by all of its elements (each an Object)
* in the proper order.
*/
private voidwriteObject(java.io.ObjectOutputStream s)
throwsjava.io.IOException {
s.defaultWriteObject();
Object[] elements = getArray();
// Write out array length
s.writeInt(elements.length);
// Write out all elements in the proper order.
for(Object element : elements)
s.writeObject(element);
}
/**
* Reconstitutes this list from a stream (that is, deserializes it).
*@paramsthe stream
*@throwsClassNotFoundException if the class of a serialized object
* could not be found
*@throwsjava.io.IOException if an I/O error occurs
*/
private voidreadObject(java.io.ObjectInputStream s)
throwsjava.io.IOException,ClassNotFoundException {
s.defaultReadObject();
// bind to new lock
resetLock();
// Read in array length and allocate array
intlen = s.readInt();
Object[] elements =newObject[len];
// Read in all elements in the proper order.
for(inti =0;i < len;i++)
elements[i] = s.readObject();
setArray(elements);
}
/**
* Returns a string representation of this list. The string
* representation consists of the string representations of the list's
* elements in the order they are returned by its iterator, enclosed in
* square brackets ({@code"[]"}). Adjacent elements are separated by
* the characters {@code", "} (comma and space). Elements are
* converted to strings as by {@linkString#valueOf(Object)}.
*
*@returna string representation of this list
*/
publicStringtoString() {
returnArrays.toString(getArray());
}
/**
* Compares the specified object with this list for equality.
* Returns {@codetrue} if the specified object is the same object
* as this object, or if it is also a {@linkList} and the sequence
* of elements returned by an {@linkplainList#iterator() iterator}
* over the specified list is the same as the sequence returned by
* an iterator over this list. The two sequences are considered to
* be the same if they have the same length and corresponding
* elements at the same position in the sequence areequal.
* Two elements {@codee1} and {@codee2} are considered
*equalif {@code(e1==null ? e2==null : e1.equals(e2))}.
*
*@paramothe object to be compared for equality with this list
*@return{@codetrue} if the specified object is equal to this list
*/
public booleanequals(Object o) {
if(o ==this)
return true;
if(!(oinstanceofList))
return false;
List list = (List)(o);
Iterator it = list.iterator();
Object[] elements = getArray();
intlen = elements.length;
for(inti =0;i < len;++i)
if(!it.hasNext() || !eq(elements[i],it.next()))
return false;
if(it.hasNext())
return false;
return true;
}
/**
* Returns the hash code value for this list.
*
*
This implementation uses the definition in {@linkList#hashCode}.
*
*@returnthe hash code value for this list
*/
public inthashCode() {
inthashCode =1;
Object[] elements = getArray();
intlen = elements.length;
for(inti =0;i < len;++i) {
Object obj = elements[i];
hashCode =31*hashCode + (obj==null?0: obj.hashCode());
}
returnhashCode;
}
/**
* Returns an iterator over the elements in this list in proper sequence.
*
*
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 doesNOTsupport the
* {@coderemove} method.
*
*@returnan iterator over the elements in this list in proper sequence
*/
publicIteratoriterator() {
return newCOWIterator(getArray(),0);
}
/**
* {@inheritDoc}
*
*
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 doesNOTsupport the
* {@coderemove}, {@codeset} or {@codeadd} methods.
*/
publicListIteratorlistIterator() {
return newCOWIterator(getArray(),0);
}
/**
* {@inheritDoc}
*
*
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 doesNOTsupport the
* {@coderemove}, {@codeset} or {@codeadd} methods.
*
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
publicListIteratorlistIterator(intindex) {
Object[] elements = getArray();
intlen = elements.length;
if(index <0|| index > len)
throw newIndexOutOfBoundsException("Index: "+index);
return newCOWIterator(elements,index);
}
/**
* Returns a {@linkSpliterator} over the elements in this list.
*
*
The {@codeSpliterator} reports {@linkSpliterator#IMMUTABLE},
* {@linkSpliterator#ORDERED}, {@linkSpliterator#SIZED}, and
* {@linkSpliterator#SUBSIZED}.
*
*
The spliterator provides a snapshot of the state of the list
* when the spliterator was constructed. No synchronization is needed while
* operating on the spliterator.
*
*@returna {@codeSpliterator} over the elements in this list
*@since1.8
*/
publicSpliteratorspliterator() {
returnSpliterators.spliterator
(getArray(),Spliterator.IMMUTABLE| Spliterator.ORDERED);
}
static final classCOWIteratorimplementsListIterator {
/** Snapshot of the array */
private finalObject[]snapshot;
/** Index of element to be returned by subsequent call to next. */
private intcursor;
privateCOWIterator(Object[] elements, intinitialCursor) {
cursor= initialCursor;
snapshot= elements;
}
public booleanhasNext() {
returncursor
}
public booleanhasPrevious() {
returncursor>0;
}
@SuppressWarnings("unchecked")
publicEnext() {
if(! hasNext())
throw newNoSuchElementException();
return(E)snapshot[cursor++];
}
@SuppressWarnings("unchecked")
publicEprevious() {
if(! hasPrevious())
throw newNoSuchElementException();
return(E)snapshot[--cursor];
}
public intnextIndex() {
returncursor;
}
public intpreviousIndex() {
returncursor-1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
*@throwsUnsupportedOperationException always; {@coderemove}
* is not supported by this iterator.
*/
public voidremove() {
throw newUnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
*@throwsUnsupportedOperationException always; {@codeset}
* is not supported by this iterator.
*/
public voidset(Ee) {
throw newUnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
*@throwsUnsupportedOperationException always; {@codeadd}
* is not supported by this iterator.
*/
public voidadd(Ee) {
throw newUnsupportedOperationException();
}
@Override
public voidforEachRemaining(Consumer action) {
Objects.requireNonNull(action);
Object[] elements =snapshot;
final intsize = elements.length;
for(inti =cursor;i < size;i++) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
action.accept(e);
}
cursor= size;
}
}
/**
* Returns a view of the portion of this list between
* {@codefromIndex}, inclusive, and {@codetoIndex}, exclusive.
* The returned list is backed by this list, so changes in the
* returned list are reflected in this list.
*
*
The semantics of the list returned by this method become
* undefined if the backing list (i.e., this list) is modified in
* any way other than via the returned list.
*
*@paramfromIndexlow endpoint (inclusive) of the subList
*@paramtoIndexhigh endpoint (exclusive) of the subList
*@returna view of the specified range within this list
*@throwsIndexOutOfBoundsException {@inheritDoc}
*/
publicListsubList(intfromIndex, inttoIndex) {
finalReentrantLock lock =this.lock;
lock.lock();
try{
Object[] elements = getArray();
intlen = elements.length;
if(fromIndex <0|| toIndex > len || fromIndex > toIndex)
throw newIndexOutOfBoundsException();
return newCOWSubList(this,fromIndex,toIndex);
}finally{
lock.unlock();
}
}
/**
* 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 classCOWSubList
extendsAbstractList
implementsRandomAccess
{
private finalCopyOnWriteArrayListl;
private final intoffset;
private intsize;
privateObject[]expectedArray;
// only call this holding l's lock
COWSubList(CopyOnWriteArrayList list,
intfromIndex, inttoIndex) {
l= list;
expectedArray=l.getArray();
offset= fromIndex;
size= toIndex - fromIndex;
}
// only call this holding l's lock
private voidcheckForComodification() {
if(l.getArray() !=expectedArray)
throw newConcurrentModificationException();
}
// only call this holding l's lock
private voidrangeCheck(intindex) {
if(index <0|| index >=size)
throw newIndexOutOfBoundsException("Index: "+index+
",Size: "+size);
}
publicEset(intindex,Eelement) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
rangeCheck(index);
checkForComodification();
Ex =l.set(index+offset,element);
expectedArray=l.getArray();
returnx;
}finally{
lock.unlock();
}
}
publicEget(intindex) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
rangeCheck(index);
checkForComodification();
returnl.get(index+offset);
}finally{
lock.unlock();
}
}
public intsize() {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
returnsize;
}finally{
lock.unlock();
}
}
public voidadd(intindex,Eelement) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
if(index <0|| index >size)
throw newIndexOutOfBoundsException();
l.add(index+offset,element);
expectedArray=l.getArray();
size++;
}finally{
lock.unlock();
}
}
public voidclear() {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
l.removeRange(offset,offset+size);
expectedArray=l.getArray();
size=0;
}finally{
lock.unlock();
}
}
publicEremove(intindex) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
rangeCheck(index);
checkForComodification();
Eresult =l.remove(index+offset);
expectedArray=l.getArray();
size--;
returnresult;
}finally{
lock.unlock();
}
}
public booleanremove(Object o) {
intindex = indexOf(o);
if(index == -1)
return false;
remove(index);
return true;
}
publicIteratoriterator() {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
return newCOWSubListIterator(l,0,offset,size);
}finally{
lock.unlock();
}
}
publicListIteratorlistIterator(intindex) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
if(index <0|| index >size)
throw newIndexOutOfBoundsException("Index: "+index+
", Size: "+size);
return newCOWSubListIterator(l,index,offset,size);
}finally{
lock.unlock();
}
}
publicListsubList(intfromIndex, inttoIndex) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
checkForComodification();
if(fromIndex <0|| toIndex >size|| fromIndex > toIndex)
throw newIndexOutOfBoundsException();
return newCOWSubList(l,fromIndex +offset,
toIndex +offset);
}finally{
lock.unlock();
}
}
public voidforEach(Consumer action) {
if(action ==null)throw newNullPointerException();
intlo =offset;
inthi =offset+size;
Object[] a =expectedArray;
if(l.getArray() != a)
throw newConcurrentModificationException();
if(lo <0|| hi > a.length)
throw newIndexOutOfBoundsException();
for(inti = lo;i < hi;++i) {
@SuppressWarnings("unchecked")Ee = (E) a[i];
action.accept(e);
}
}
public voidreplaceAll(UnaryOperator operator) {
if(operator ==null)throw newNullPointerException();
finalReentrantLock lock =l.lock;
lock.lock();
try{
intlo =offset;
inthi =offset+size;
Object[] elements =expectedArray;
if(l.getArray() != elements)
throw newConcurrentModificationException();
intlen = elements.length;
if(lo <0|| hi > len)
throw newIndexOutOfBoundsException();
Object[] newElements = Arrays.copyOf(elements,len);
for(inti = lo;i < hi;++i) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
newElements[i] = operator.apply(e);
}
l.setArray(expectedArray= newElements);
}finally{
lock.unlock();
}
}
public voidsort(Comparator c) {
finalReentrantLock lock =l.lock;
lock.lock();
try{
intlo =offset;
inthi =offset+size;
Object[] elements =expectedArray;
if(l.getArray() != elements)
throw newConcurrentModificationException();
intlen = elements.length;
if(lo <0|| hi > len)
throw newIndexOutOfBoundsException();
Object[] newElements = Arrays.copyOf(elements,len);
@SuppressWarnings("unchecked")E[] es = (E[])newElements;
Arrays.sort(es,lo,hi,c);
l.setArray(expectedArray= newElements);
}finally{
lock.unlock();
}
}
public booleanremoveAll(Collection c) {
if(c ==null)throw newNullPointerException();
booleanremoved =false;
finalReentrantLock lock =l.lock;
lock.lock();
try{
intn =size;
if(n >0) {
intlo =offset;
inthi =offset+ n;
Object[] elements =expectedArray;
if(l.getArray() != elements)
throw newConcurrentModificationException();
intlen = elements.length;
if(lo <0|| hi > len)
throw newIndexOutOfBoundsException();
intnewSize =0;
Object[] temp =newObject[n];
for(inti = lo;i < hi;++i) {
Object element = elements[i];
if(!c.contains(element))
temp[newSize++] = element;
}
if(newSize != n) {
Object[] newElements =newObject[len - n + newSize];
System.arraycopy(elements,0,newElements,0,lo);
System.arraycopy(temp,0,newElements,lo,newSize);
System.arraycopy(elements,hi,newElements,
lo + newSize,len - hi);
size= newSize;
removed =true;
l.setArray(expectedArray= newElements);
}
}
}finally{
lock.unlock();
}
returnremoved;
}
public booleanretainAll(Collection c) {
if(c ==null)throw newNullPointerException();
booleanremoved =false;
finalReentrantLock lock =l.lock;
lock.lock();
try{
intn =size;
if(n >0) {
intlo =offset;
inthi =offset+ n;
Object[] elements =expectedArray;
if(l.getArray() != elements)
throw newConcurrentModificationException();
intlen = elements.length;
if(lo <0|| hi > len)
throw newIndexOutOfBoundsException();
intnewSize =0;
Object[] temp =newObject[n];
for(inti = lo;i < hi;++i) {
Object element = elements[i];
if(c.contains(element))
temp[newSize++] = element;
}
if(newSize != n) {
Object[] newElements =newObject[len - n + newSize];
System.arraycopy(elements,0,newElements,0,lo);
System.arraycopy(temp,0,newElements,lo,newSize);
System.arraycopy(elements,hi,newElements,
lo + newSize,len - hi);
size= newSize;
removed =true;
l.setArray(expectedArray= newElements);
}
}
}finally{
lock.unlock();
}
returnremoved;
}
public booleanremoveIf(Predicate filter) {
if(filter ==null)throw newNullPointerException();
booleanremoved =false;
finalReentrantLock lock =l.lock;
lock.lock();
try{
intn =size;
if(n >0) {
intlo =offset;
inthi =offset+ n;
Object[] elements =expectedArray;
if(l.getArray() != elements)
throw newConcurrentModificationException();
intlen = elements.length;
if(lo <0|| hi > len)
throw newIndexOutOfBoundsException();
intnewSize =0;
Object[] temp =newObject[n];
for(inti = lo;i < hi;++i) {
@SuppressWarnings("unchecked")Ee = (E) elements[i];
if(!filter.test(e))
temp[newSize++] = e;
}
if(newSize != n) {
Object[] newElements =newObject[len - n + newSize];
System.arraycopy(elements,0,newElements,0,lo);
System.arraycopy(temp,0,newElements,lo,newSize);
System.arraycopy(elements,hi,newElements,
lo + newSize,len - hi);
size= newSize;
removed =true;
l.setArray(expectedArray= newElements);
}
}
}finally{
lock.unlock();
}
returnremoved;
}
publicSpliteratorspliterator() {
intlo =offset;
inthi =offset+size;
Object[] a =expectedArray;
if(l.getArray() != a)
throw newConcurrentModificationException();
if(lo <0|| hi > a.length)
throw newIndexOutOfBoundsException();
returnSpliterators.spliterator
(a,lo,hi,Spliterator.IMMUTABLE| Spliterator.ORDERED);
}
}
private static classCOWSubListIteratorimplementsListIterator {
private finalListIteratorit;
private final intoffset;
private final intsize;
COWSubListIterator(List l, intindex, intoffset, intsize) {
this.offset= offset;
this.size= size;
it= l.listIterator(index+offset);
}
public booleanhasNext() {
returnnextIndex()
}
publicEnext() {
if(hasNext())
returnit.next();
else
throw newNoSuchElementException();
}
public booleanhasPrevious() {
returnpreviousIndex() >=0;
}
publicEprevious() {
if(hasPrevious())
returnit.previous();
else
throw newNoSuchElementException();
}
public intnextIndex() {
returnit.nextIndex() -offset;
}
public intpreviousIndex() {
returnit.previousIndex() -offset;
}
public voidremove() {
throw newUnsupportedOperationException();
}
public voidset(Ee) {
throw newUnsupportedOperationException();
}
public voidadd(Ee) {
throw newUnsupportedOperationException();
}
@Override
public voidforEachRemaining(Consumer action) {
Objects.requireNonNull(action);
ints =size;
ListIterator i =it;
while(nextIndex() < s) {
action.accept(i.next());
}
}
}
// Support for resetting lock while deserializing
private voidresetLock() {
UNSAFE.putObjectVolatile(this,lockOffset, newReentrantLock());
}
private static finalsun.misc.UnsafeUNSAFE;
private static final longlockOffset;
static{
try{
UNSAFE= sun.misc.Unsafe.getUnsafe();
Class k = CopyOnWriteArrayList.class;
lockOffset=UNSAFE.objectFieldOffset
(k.getDeclaredField("lock"));
}catch(Exception e) {
throw newError(e);
}
}
}
```