mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8200258: Improve CopyOnWriteArrayList subList code
Reviewed-by: martin, psandoz, smarks
This commit is contained in:
parent
f1e4c3cf6b
commit
a4f3a1d9eb
9 changed files with 1383 additions and 763 deletions
|
@ -35,7 +35,6 @@
|
|||
package java.util.concurrent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
|
@ -134,17 +133,17 @@ public class CopyOnWriteArrayList<E>
|
|||
* @throws NullPointerException if the specified collection is null
|
||||
*/
|
||||
public CopyOnWriteArrayList(Collection<? extends E> c) {
|
||||
Object[] elements;
|
||||
Object[] es;
|
||||
if (c.getClass() == CopyOnWriteArrayList.class)
|
||||
elements = ((CopyOnWriteArrayList<?>)c).getArray();
|
||||
es = ((CopyOnWriteArrayList<?>)c).getArray();
|
||||
else {
|
||||
elements = c.toArray();
|
||||
es = c.toArray();
|
||||
// defend against c.toArray (incorrectly) not returning Object[]
|
||||
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
|
||||
if (elements.getClass() != Object[].class)
|
||||
elements = Arrays.copyOf(elements, elements.length, Object[].class);
|
||||
if (es.getClass() != Object[].class)
|
||||
es = Arrays.copyOf(es, es.length, Object[].class);
|
||||
}
|
||||
setArray(elements);
|
||||
setArray(es);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,20 +179,19 @@ public class CopyOnWriteArrayList<E>
|
|||
* static version of indexOf, to allow repeated calls without
|
||||
* needing to re-acquire array each time.
|
||||
* @param o element to search for
|
||||
* @param elements the array
|
||||
* @param index first index to search
|
||||
* @param fence one past last index to search
|
||||
* @param es the array
|
||||
* @param from first index to search
|
||||
* @param to one past last index to search
|
||||
* @return index of element, or -1 if absent
|
||||
*/
|
||||
private static int indexOf(Object o, Object[] elements,
|
||||
int index, int fence) {
|
||||
private static int indexOfRange(Object o, Object[] es, int from, int to) {
|
||||
if (o == null) {
|
||||
for (int i = index; i < fence; i++)
|
||||
if (elements[i] == null)
|
||||
for (int i = from; i < to; i++)
|
||||
if (es[i] == null)
|
||||
return i;
|
||||
} else {
|
||||
for (int i = index; i < fence; i++)
|
||||
if (o.equals(elements[i]))
|
||||
for (int i = from; i < to; i++)
|
||||
if (o.equals(es[i]))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
@ -202,18 +200,19 @@ public class CopyOnWriteArrayList<E>
|
|||
/**
|
||||
* static version of lastIndexOf.
|
||||
* @param o element to search for
|
||||
* @param elements the array
|
||||
* @param index first index to search
|
||||
* @param es the array
|
||||
* @param from index of first element of range, last element to search
|
||||
* @param to one past last element of range, first element to search
|
||||
* @return index of element, or -1 if absent
|
||||
*/
|
||||
private static int lastIndexOf(Object o, Object[] elements, int index) {
|
||||
private static int lastIndexOfRange(Object o, Object[] es, int from, int to) {
|
||||
if (o == null) {
|
||||
for (int i = index; i >= 0; i--)
|
||||
if (elements[i] == null)
|
||||
for (int i = to - 1; i >= from; i--)
|
||||
if (es[i] == null)
|
||||
return i;
|
||||
} else {
|
||||
for (int i = index; i >= 0; i--)
|
||||
if (o.equals(elements[i]))
|
||||
for (int i = to - 1; i >= from; i--)
|
||||
if (o.equals(es[i]))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
@ -228,16 +227,15 @@ public class CopyOnWriteArrayList<E>
|
|||
* @return {@code true} if this list contains the specified element
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
Object[] elements = getArray();
|
||||
return indexOf(o, elements, 0, elements.length) >= 0;
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int indexOf(Object o) {
|
||||
Object[] elements = getArray();
|
||||
return indexOf(o, elements, 0, elements.length);
|
||||
Object[] es = getArray();
|
||||
return indexOfRange(o, es, 0, es.length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,16 +254,16 @@ public class CopyOnWriteArrayList<E>
|
|||
* @throws IndexOutOfBoundsException if the specified index is negative
|
||||
*/
|
||||
public int indexOf(E e, int index) {
|
||||
Object[] elements = getArray();
|
||||
return indexOf(e, elements, index, elements.length);
|
||||
Object[] es = getArray();
|
||||
return indexOfRange(e, es, index, es.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lastIndexOf(Object o) {
|
||||
Object[] elements = getArray();
|
||||
return lastIndexOf(o, elements, elements.length - 1);
|
||||
Object[] es = getArray();
|
||||
return lastIndexOfRange(o, es, 0, es.length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,8 +283,8 @@ public class CopyOnWriteArrayList<E>
|
|||
* than or equal to the current size of this list
|
||||
*/
|
||||
public int lastIndexOf(E e, int index) {
|
||||
Object[] elements = getArray();
|
||||
return lastIndexOf(e, elements, index);
|
||||
Object[] es = getArray();
|
||||
return lastIndexOfRange(e, es, 0, index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -322,8 +320,7 @@ public class CopyOnWriteArrayList<E>
|
|||
* @return an array containing all the elements in this list
|
||||
*/
|
||||
public Object[] toArray() {
|
||||
Object[] elements = getArray();
|
||||
return Arrays.copyOf(elements, elements.length);
|
||||
return getArray().clone();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -366,12 +363,12 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] a) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
if (a.length < len)
|
||||
return (T[]) Arrays.copyOf(elements, len, a.getClass());
|
||||
return (T[]) Arrays.copyOf(es, len, a.getClass());
|
||||
else {
|
||||
System.arraycopy(elements, 0, a, 0, len);
|
||||
System.arraycopy(es, 0, a, 0, len);
|
||||
if (a.length > len)
|
||||
a[len] = null;
|
||||
return a;
|
||||
|
@ -406,17 +403,13 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public E set(int index, E element) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
E oldValue = elementAt(elements, index);
|
||||
Object[] es = getArray();
|
||||
E oldValue = elementAt(es, 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);
|
||||
es = es.clone();
|
||||
es[index] = element;
|
||||
setArray(es);
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
@ -430,11 +423,11 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public boolean add(E e) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] newElements = Arrays.copyOf(elements, len + 1);
|
||||
newElements[len] = e;
|
||||
setArray(newElements);
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
es = Arrays.copyOf(es, len + 1);
|
||||
es[len] = e;
|
||||
setArray(es);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -448,18 +441,18 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public void add(int index, E element) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
if (index > len || index < 0)
|
||||
throw new IndexOutOfBoundsException(outOfBounds(index, len));
|
||||
Object[] newElements;
|
||||
int numMoved = len - index;
|
||||
if (numMoved == 0)
|
||||
newElements = Arrays.copyOf(elements, len + 1);
|
||||
newElements = Arrays.copyOf(es, len + 1);
|
||||
else {
|
||||
newElements = new Object[len + 1];
|
||||
System.arraycopy(elements, 0, newElements, 0, index);
|
||||
System.arraycopy(elements, index, newElements, index + 1,
|
||||
System.arraycopy(es, 0, newElements, 0, index);
|
||||
System.arraycopy(es, index, newElements, index + 1,
|
||||
numMoved);
|
||||
}
|
||||
newElements[index] = element;
|
||||
|
@ -476,19 +469,20 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public E remove(int index) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
E oldValue = elementAt(elements, index);
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
E oldValue = elementAt(es, index);
|
||||
int numMoved = len - index - 1;
|
||||
Object[] newElements;
|
||||
if (numMoved == 0)
|
||||
setArray(Arrays.copyOf(elements, len - 1));
|
||||
newElements = Arrays.copyOf(es, len - 1);
|
||||
else {
|
||||
Object[] newElements = new Object[len - 1];
|
||||
System.arraycopy(elements, 0, newElements, 0, index);
|
||||
System.arraycopy(elements, index + 1, newElements, index,
|
||||
newElements = new Object[len - 1];
|
||||
System.arraycopy(es, 0, newElements, 0, index);
|
||||
System.arraycopy(es, index + 1, newElements, index,
|
||||
numMoved);
|
||||
setArray(newElements);
|
||||
}
|
||||
setArray(newElements);
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +501,7 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public boolean remove(Object o) {
|
||||
Object[] snapshot = getArray();
|
||||
int index = indexOf(o, snapshot, 0, snapshot.length);
|
||||
int index = indexOfRange(o, snapshot, 0, snapshot.length);
|
||||
return index >= 0 && remove(o, snapshot, index);
|
||||
}
|
||||
|
||||
|
@ -532,7 +526,7 @@ public class CopyOnWriteArrayList<E>
|
|||
return false;
|
||||
if (current[index] == o)
|
||||
break findIndex;
|
||||
index = indexOf(o, current, index, len);
|
||||
index = indexOfRange(o, current, index, len);
|
||||
if (index < 0)
|
||||
return false;
|
||||
}
|
||||
|
@ -560,19 +554,19 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
void removeRange(int fromIndex, int toIndex) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.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));
|
||||
setArray(Arrays.copyOf(es, newlen));
|
||||
else {
|
||||
Object[] newElements = new Object[newlen];
|
||||
System.arraycopy(elements, 0, newElements, 0, fromIndex);
|
||||
System.arraycopy(elements, toIndex, newElements,
|
||||
System.arraycopy(es, 0, newElements, 0, fromIndex);
|
||||
System.arraycopy(es, toIndex, newElements,
|
||||
fromIndex, numMoved);
|
||||
setArray(newElements);
|
||||
}
|
||||
|
@ -587,7 +581,7 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public boolean addIfAbsent(E e) {
|
||||
Object[] snapshot = getArray();
|
||||
return indexOf(e, snapshot, 0, snapshot.length) < 0
|
||||
return indexOfRange(e, snapshot, 0, snapshot.length) < 0
|
||||
&& addIfAbsent(e, snapshot);
|
||||
}
|
||||
|
||||
|
@ -606,7 +600,7 @@ public class CopyOnWriteArrayList<E>
|
|||
if (current[i] != snapshot[i]
|
||||
&& Objects.equals(e, current[i]))
|
||||
return false;
|
||||
if (indexOf(e, current, common, len) >= 0)
|
||||
if (indexOfRange(e, current, common, len) >= 0)
|
||||
return false;
|
||||
}
|
||||
Object[] newElements = Arrays.copyOf(current, len + 1);
|
||||
|
@ -627,10 +621,10 @@ public class CopyOnWriteArrayList<E>
|
|||
* @see #contains(Object)
|
||||
*/
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
for (Object e : c) {
|
||||
if (indexOf(e, elements, 0, len) < 0)
|
||||
if (indexOfRange(e, es, 0, len) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -694,18 +688,18 @@ public class CopyOnWriteArrayList<E>
|
|||
if (cs.length == 0)
|
||||
return 0;
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
int added = 0;
|
||||
// uniquify and compact elements in cs
|
||||
for (int i = 0; i < cs.length; ++i) {
|
||||
Object e = cs[i];
|
||||
if (indexOf(e, elements, 0, len) < 0 &&
|
||||
indexOf(e, cs, 0, added) < 0)
|
||||
if (indexOfRange(e, es, 0, len) < 0 &&
|
||||
indexOfRange(e, cs, 0, added) < 0)
|
||||
cs[added++] = e;
|
||||
}
|
||||
if (added > 0) {
|
||||
Object[] newElements = Arrays.copyOf(elements, len + added);
|
||||
Object[] newElements = Arrays.copyOf(es, len + added);
|
||||
System.arraycopy(cs, 0, newElements, len, added);
|
||||
setArray(newElements);
|
||||
}
|
||||
|
@ -739,15 +733,16 @@ public class CopyOnWriteArrayList<E>
|
|||
if (cs.length == 0)
|
||||
return false;
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
Object[] newElements;
|
||||
if (len == 0 && cs.getClass() == Object[].class)
|
||||
setArray(cs);
|
||||
newElements = cs;
|
||||
else {
|
||||
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
|
||||
newElements = Arrays.copyOf(es, len + cs.length);
|
||||
System.arraycopy(cs, 0, newElements, len, cs.length);
|
||||
setArray(newElements);
|
||||
}
|
||||
setArray(newElements);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -771,8 +766,8 @@ public class CopyOnWriteArrayList<E>
|
|||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
Object[] cs = c.toArray();
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
if (index > len || index < 0)
|
||||
throw new IndexOutOfBoundsException(outOfBounds(index, len));
|
||||
if (cs.length == 0)
|
||||
|
@ -780,11 +775,11 @@ public class CopyOnWriteArrayList<E>
|
|||
int numMoved = len - index;
|
||||
Object[] newElements;
|
||||
if (numMoved == 0)
|
||||
newElements = Arrays.copyOf(elements, len + cs.length);
|
||||
newElements = Arrays.copyOf(es, len + cs.length);
|
||||
else {
|
||||
newElements = new Object[len + cs.length];
|
||||
System.arraycopy(elements, 0, newElements, 0, index);
|
||||
System.arraycopy(elements, index,
|
||||
System.arraycopy(es, 0, newElements, 0, index);
|
||||
System.arraycopy(es, index,
|
||||
newElements, index + cs.length,
|
||||
numMoved);
|
||||
}
|
||||
|
@ -866,14 +861,14 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
public void replaceAll(UnaryOperator<E> operator) {
|
||||
Objects.requireNonNull(operator);
|
||||
synchronized (lock) {
|
||||
replaceAll(operator, 0, getArray().length);
|
||||
replaceAllRange(operator, 0, getArray().length);
|
||||
}
|
||||
}
|
||||
|
||||
void replaceAll(UnaryOperator<E> operator, int i, int end) {
|
||||
void replaceAllRange(UnaryOperator<E> operator, int i, int end) {
|
||||
// assert Thread.holdsLock(lock);
|
||||
Objects.requireNonNull(operator);
|
||||
final Object[] es = getArray().clone();
|
||||
for (; i < end; i++)
|
||||
es[i] = operator.apply(elementAt(es, i));
|
||||
|
@ -882,12 +877,12 @@ public class CopyOnWriteArrayList<E>
|
|||
|
||||
public void sort(Comparator<? super E> c) {
|
||||
synchronized (lock) {
|
||||
sort(c, 0, getArray().length);
|
||||
sortRange(c, 0, getArray().length);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void sort(Comparator<? super E> c, int i, int end) {
|
||||
void sortRange(Comparator<? super E> c, int i, int end) {
|
||||
// assert Thread.holdsLock(lock);
|
||||
final Object[] es = getArray().clone();
|
||||
Arrays.sort(es, i, end, (Comparator<Object>)c);
|
||||
|
@ -908,12 +903,12 @@ public class CopyOnWriteArrayList<E>
|
|||
|
||||
s.defaultWriteObject();
|
||||
|
||||
Object[] elements = getArray();
|
||||
Object[] es = getArray();
|
||||
// Write out array length
|
||||
s.writeInt(elements.length);
|
||||
s.writeInt(es.length);
|
||||
|
||||
// Write out all elements in the proper order.
|
||||
for (Object element : elements)
|
||||
for (Object element : es)
|
||||
s.writeObject(element);
|
||||
}
|
||||
|
||||
|
@ -935,12 +930,12 @@ public class CopyOnWriteArrayList<E>
|
|||
// Read in array length and allocate array
|
||||
int len = s.readInt();
|
||||
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, len);
|
||||
Object[] elements = new Object[len];
|
||||
Object[] es = new Object[len];
|
||||
|
||||
// Read in all elements in the proper order.
|
||||
for (int i = 0; i < len; i++)
|
||||
elements[i] = s.readObject();
|
||||
setArray(elements);
|
||||
es[i] = s.readObject();
|
||||
setArray(es);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -986,6 +981,15 @@ public class CopyOnWriteArrayList<E>
|
|||
return !it.hasNext();
|
||||
}
|
||||
|
||||
private static int hashCodeOfRange(Object[] es, int from, int to) {
|
||||
int hashCode = 1;
|
||||
for (int i = from; i < to; i++) {
|
||||
Object x = es[i];
|
||||
hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this list.
|
||||
*
|
||||
|
@ -994,10 +998,8 @@ public class CopyOnWriteArrayList<E>
|
|||
* @return the hash code value for this list
|
||||
*/
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
for (Object x : getArray())
|
||||
hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
|
||||
return hashCode;
|
||||
Object[] es = getArray();
|
||||
return hashCodeOfRange(es, 0, es.length);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1037,12 +1039,12 @@ public class CopyOnWriteArrayList<E>
|
|||
* @throws IndexOutOfBoundsException {@inheritDoc}
|
||||
*/
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
if (index < 0 || index > len)
|
||||
throw new IndexOutOfBoundsException(outOfBounds(index, len));
|
||||
|
||||
return new COWIterator<E>(elements, index);
|
||||
return new COWIterator<E>(es, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1070,9 +1072,9 @@ public class CopyOnWriteArrayList<E>
|
|||
/** Index of element to be returned by subsequent call to next. */
|
||||
private int cursor;
|
||||
|
||||
COWIterator(Object[] elements, int initialCursor) {
|
||||
COWIterator(Object[] es, int initialCursor) {
|
||||
cursor = initialCursor;
|
||||
snapshot = elements;
|
||||
snapshot = es;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
|
@ -1102,7 +1104,7 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
public int previousIndex() {
|
||||
return cursor-1;
|
||||
return cursor - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1133,14 +1135,13 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void forEachRemaining(Consumer<? super E> action) {
|
||||
Objects.requireNonNull(action);
|
||||
final int size = snapshot.length;
|
||||
for (int i = cursor; i < size; i++) {
|
||||
action.accept((E) snapshot[i]);
|
||||
}
|
||||
int i = cursor;
|
||||
cursor = size;
|
||||
for (; i < size; i++)
|
||||
action.accept(elementAt(snapshot, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1161,136 +1162,264 @@ public class CopyOnWriteArrayList<E>
|
|||
*/
|
||||
public List<E> subList(int fromIndex, int toIndex) {
|
||||
synchronized (lock) {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
|
||||
Object[] es = getArray();
|
||||
int len = es.length;
|
||||
int size = toIndex - fromIndex;
|
||||
if (fromIndex < 0 || toIndex > len || size < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return new COWSubList<E>(this, fromIndex, toIndex);
|
||||
return new COWSubList(es, fromIndex, size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sublist for CopyOnWriteArrayList.
|
||||
*/
|
||||
private static class COWSubList<E>
|
||||
extends AbstractList<E>
|
||||
implements RandomAccess
|
||||
{
|
||||
private final CopyOnWriteArrayList<E> l;
|
||||
private class COWSubList implements List<E>, RandomAccess {
|
||||
private final int offset;
|
||||
private int size;
|
||||
private Object[] expectedArray;
|
||||
|
||||
// only call this holding l's lock
|
||||
COWSubList(CopyOnWriteArrayList<E> list,
|
||||
int fromIndex, int toIndex) {
|
||||
// assert Thread.holdsLock(list.lock);
|
||||
l = list;
|
||||
expectedArray = l.getArray();
|
||||
offset = fromIndex;
|
||||
size = toIndex - fromIndex;
|
||||
COWSubList(Object[] es, int offset, int size) {
|
||||
// assert Thread.holdsLock(lock);
|
||||
expectedArray = es;
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
// only call this holding l's lock
|
||||
private void checkForComodification() {
|
||||
// assert Thread.holdsLock(l.lock);
|
||||
if (l.getArray() != expectedArray)
|
||||
// assert Thread.holdsLock(lock);
|
||||
if (getArray() != expectedArray)
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
private Object[] getArrayChecked() {
|
||||
// assert Thread.holdsLock(l.lock);
|
||||
Object[] a = l.getArray();
|
||||
// assert Thread.holdsLock(lock);
|
||||
Object[] a = getArray();
|
||||
if (a != expectedArray)
|
||||
throw new ConcurrentModificationException();
|
||||
return a;
|
||||
}
|
||||
|
||||
// only call this holding l's lock
|
||||
private void rangeCheck(int index) {
|
||||
// assert Thread.holdsLock(l.lock);
|
||||
// assert Thread.holdsLock(lock);
|
||||
if (index < 0 || index >= size)
|
||||
throw new IndexOutOfBoundsException(outOfBounds(index, size));
|
||||
}
|
||||
|
||||
private void rangeCheckForAdd(int index) {
|
||||
// assert Thread.holdsLock(lock);
|
||||
if (index < 0 || index > size)
|
||||
throw new IndexOutOfBoundsException(outOfBounds(index, size));
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
return Arrays.copyOfRange(es, offset, offset + size);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] a) {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
if (a.length < size)
|
||||
return (T[]) Arrays.copyOfRange(
|
||||
es, offset, offset + size, a.getClass());
|
||||
else {
|
||||
System.arraycopy(es, offset, a, 0, size);
|
||||
if (a.length > size)
|
||||
a[size] = null;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
public int indexOf(Object o) {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
int i = indexOfRange(o, es, offset, offset + size);
|
||||
return (i == -1) ? -1 : i - offset;
|
||||
}
|
||||
|
||||
public int lastIndexOf(Object o) {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
int i = lastIndexOfRange(o, es, offset, offset + size);
|
||||
return (i == -1) ? -1 : i - offset;
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return indexOf(o) >= 0;
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
for (Object o : c)
|
||||
if (indexOfRange(o, es, offset, offset + size) < 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Arrays.toString(toArray());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
return hashCodeOfRange(es, offset, offset + size);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof List))
|
||||
return false;
|
||||
Iterator<?> it = ((List<?>)o).iterator();
|
||||
|
||||
final Object[] es;
|
||||
final int offset;
|
||||
final int size;
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
offset = this.offset;
|
||||
size = this.size;
|
||||
}
|
||||
|
||||
for (int i = offset, end = offset + size; i < end; i++)
|
||||
if (!it.hasNext() || !Objects.equals(es[i], it.next()))
|
||||
return false;
|
||||
return !it.hasNext();
|
||||
}
|
||||
|
||||
public E set(int index, E element) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
rangeCheck(index);
|
||||
checkForComodification();
|
||||
E x = l.set(offset + index, element);
|
||||
expectedArray = l.getArray();
|
||||
E x = CopyOnWriteArrayList.this.set(offset + index, element);
|
||||
expectedArray = getArray();
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public E get(int index) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
rangeCheck(index);
|
||||
checkForComodification();
|
||||
return l.get(offset + index);
|
||||
return CopyOnWriteArrayList.this.get(offset + index);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(E element) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
l.add(offset + size, element);
|
||||
expectedArray = l.getArray();
|
||||
CopyOnWriteArrayList.this.add(offset + size, element);
|
||||
expectedArray = getArray();
|
||||
size++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void add(int index, E element) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
if (index < 0 || index > size)
|
||||
throw new IndexOutOfBoundsException
|
||||
(outOfBounds(index, size));
|
||||
l.add(offset + index, element);
|
||||
expectedArray = l.getArray();
|
||||
rangeCheckForAdd(index);
|
||||
CopyOnWriteArrayList.this.add(offset + index, element);
|
||||
expectedArray = getArray();
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> c) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
final Object[] oldArray = getArrayChecked();
|
||||
boolean modified = l.addAll(offset + size, c);
|
||||
size += (expectedArray = l.getArray()).length - oldArray.length;
|
||||
boolean modified =
|
||||
CopyOnWriteArrayList.this.addAll(offset + size, c);
|
||||
size += (expectedArray = getArray()).length - oldArray.length;
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
synchronized (lock) {
|
||||
rangeCheckForAdd(index);
|
||||
final Object[] oldArray = getArrayChecked();
|
||||
boolean modified =
|
||||
CopyOnWriteArrayList.this.addAll(offset + index, c);
|
||||
size += (expectedArray = getArray()).length - oldArray.length;
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
l.removeRange(offset, offset + size);
|
||||
expectedArray = l.getArray();
|
||||
removeRange(offset, offset + size);
|
||||
expectedArray = getArray();
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public E remove(int index) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
rangeCheck(index);
|
||||
checkForComodification();
|
||||
E result = l.remove(offset + index);
|
||||
expectedArray = l.getArray();
|
||||
E result = CopyOnWriteArrayList.this.remove(offset + index);
|
||||
expectedArray = getArray();
|
||||
size--;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
int index = indexOf(o);
|
||||
if (index == -1)
|
||||
|
@ -1301,36 +1430,35 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
synchronized (l.lock) {
|
||||
checkForComodification();
|
||||
return new COWSubListIterator<E>(l, 0, offset, size);
|
||||
}
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
public ListIterator<E> listIterator() {
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
if (index < 0 || index > size)
|
||||
throw new IndexOutOfBoundsException
|
||||
(outOfBounds(index, size));
|
||||
return new COWSubListIterator<E>(l, index, offset, size);
|
||||
rangeCheckForAdd(index);
|
||||
return new COWSubListIterator<E>(
|
||||
CopyOnWriteArrayList.this, index, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
public List<E> subList(int fromIndex, int toIndex) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
if (fromIndex < 0 || toIndex > size || fromIndex > toIndex)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return new COWSubList<E>(l, fromIndex + offset,
|
||||
toIndex + offset);
|
||||
return new COWSubList(expectedArray, fromIndex + offset, toIndex - fromIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
Objects.requireNonNull(action);
|
||||
int i, end; final Object[] es;
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
es = getArrayChecked();
|
||||
i = offset;
|
||||
end = i + size;
|
||||
|
@ -1340,19 +1468,18 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
public void replaceAll(UnaryOperator<E> operator) {
|
||||
Objects.requireNonNull(operator);
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
l.replaceAll(operator, offset, offset + size);
|
||||
expectedArray = l.getArray();
|
||||
replaceAllRange(operator, offset, offset + size);
|
||||
expectedArray = getArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void sort(Comparator<? super E> c) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
checkForComodification();
|
||||
l.sort(c, offset, offset + size);
|
||||
expectedArray = l.getArray();
|
||||
sortRange(c, offset, offset + size);
|
||||
expectedArray = getArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1372,16 +1499,17 @@ public class CopyOnWriteArrayList<E>
|
|||
}
|
||||
|
||||
private boolean bulkRemove(Predicate<? super E> filter) {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
final Object[] oldArray = getArrayChecked();
|
||||
boolean modified = l.bulkRemove(filter, offset, offset + size);
|
||||
size += (expectedArray = l.getArray()).length - oldArray.length;
|
||||
boolean modified = CopyOnWriteArrayList.this.bulkRemove(
|
||||
filter, offset, offset + size);
|
||||
size += (expectedArray = getArray()).length - oldArray.length;
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
||||
public Spliterator<E> spliterator() {
|
||||
synchronized (l.lock) {
|
||||
synchronized (lock) {
|
||||
return Spliterators.spliterator(
|
||||
getArrayChecked(), offset, offset + size,
|
||||
Spliterator.IMMUTABLE | Spliterator.ORDERED);
|
||||
|
@ -1398,7 +1526,7 @@ public class CopyOnWriteArrayList<E>
|
|||
COWSubListIterator(List<E> l, int index, int offset, int size) {
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
it = l.listIterator(index+offset);
|
||||
it = l.listIterator(index + offset);
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
|
@ -1447,7 +1575,7 @@ public class CopyOnWriteArrayList<E>
|
|||
@SuppressWarnings("unchecked")
|
||||
public void forEachRemaining(Consumer<? super E> action) {
|
||||
Objects.requireNonNull(action);
|
||||
while (nextIndex() < size) {
|
||||
while (hasNext()) {
|
||||
action.accept(it.next());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue