From 059da9be12cf09f4e8975e57909c6b7cba899eb5 Mon Sep 17 00:00:00 2001 From: Speiger Date: Sat, 9 Apr 2022 03:56:54 +0200 Subject: [PATCH] New Features -Added: CopyOnWriteArrayList -Added: UnitTests for CopyOnWriteArrayLists --- .../speiger/src/builder/GlobalVariables.java | 1 + .../builder/PrimitiveCollectionsBuilder.java | 1 + .../templates/lists/AbstractList.template | 4 +- .../templates/lists/CopyOnWriteList.template | 1788 +++++++++++++++++ .../objects/list/ObjectListTests.java | 26 + 5 files changed, 1818 insertions(+), 2 deletions(-) create mode 100644 src/builder/resources/speiger/assets/collections/templates/lists/CopyOnWriteList.template diff --git a/src/builder/java/speiger/src/builder/GlobalVariables.java b/src/builder/java/speiger/src/builder/GlobalVariables.java index 69801509..20dae590 100644 --- a/src/builder/java/speiger/src/builder/GlobalVariables.java +++ b/src/builder/java/speiger/src/builder/GlobalVariables.java @@ -158,6 +158,7 @@ public class GlobalVariables //Final Classes addClassMapper("ARRAY_LIST", "ArrayList"); + addAbstractMapper("COPY_ON_WRITE_LIST", "CopyOnWrite%sArrayList"); addClassMapper("ASYNC_BUILDER", "AsyncBuilder"); addClassMapper("LINKED_LIST", "LinkedList"); addAbstractMapper("IMMUTABLE_LIST", "Immutable%sList"); diff --git a/src/builder/java/speiger/src/builder/PrimitiveCollectionsBuilder.java b/src/builder/java/speiger/src/builder/PrimitiveCollectionsBuilder.java index efebcf58..4a8eac9d 100644 --- a/src/builder/java/speiger/src/builder/PrimitiveCollectionsBuilder.java +++ b/src/builder/java/speiger/src/builder/PrimitiveCollectionsBuilder.java @@ -94,6 +94,7 @@ public class PrimitiveCollectionsBuilder extends TemplateProcessor nameRemapper.put("EnumMap", "Enum2%sMap"); nameRemapper.put("LinkedEnumMap", "LinkedEnum2%sMap"); nameRemapper.put("ImmutableList", "Immutable%sList"); + nameRemapper.put("CopyOnWriteList", "CopyOnWrite%sArrayList"); nameRemapper.put("ImmutableOpenHashSet", "Immutable%sOpenHashSet"); nameRemapper.put("ImmutableOpenHashMap", "Immutable%sOpenHashMap"); diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template b/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template index 38026770..24d62a66 100644 --- a/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template +++ b/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template @@ -75,7 +75,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION * The IndexOf implementation iterates over all elements and compares them to the search value. * @param o the value that the index is searched for. * @return index of the value that was searched for. -1 if not found - * @deprecated it is highly suggested not to use this with Primitives because of boxing. But it is still supported because of ObjectComparason that are custom objects and allow to find the contents. + * @note it is highly suggested not to use this with Primitives because of boxing. But it is still supported because of ObjectComparason that are custom objects and allow to find the contents. */ @Override @Primitive @@ -103,7 +103,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION * The lastIndexOf implementation iterates over all elements and compares them to the search value. * @param o the value that the index is searched for. * @return the last index of the value that was searched for. -1 if not found - * @deprecated it is highly suggested not to use this with Primitives because of boxing. But it is still supported because of ObjectComparason that are custom objects and allow to find the contents. + * @note it is highly suggested not to use this with Primitives because of boxing. But it is still supported because of ObjectComparason that are custom objects and allow to find the contents. */ @Override @Primitive diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/CopyOnWriteList.template b/src/builder/resources/speiger/assets/collections/templates/lists/CopyOnWriteList.template new file mode 100644 index 00000000..7fcd016f --- /dev/null +++ b/src/builder/resources/speiger/assets/collections/templates/lists/CopyOnWriteList.template @@ -0,0 +1,1788 @@ +package speiger.src.collections.PACKAGE.lists; + +import java.util.Arrays; +#if TYPE_OBJECT +import java.util.Comparator; +#endif +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +#if PRIMITIVES +import java.util.function.JAVA_PREDICATE; +import java.util.function.JAVA_UNARY_OPERATOR; +import java.nio.JAVA_BUFFER; +#endif + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.collections.STACK; +#endif +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.functions.COMPARATOR; +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.utils.ARRAYS; +import speiger.src.collections.PACKAGE.utils.ITERATORS; +#if TYPE_OBJECT +import speiger.src.collections.utils.Stack; +#else +import speiger.src.collections.objects.utils.ObjectArrays; +#endif +#if PRIMITIVES +import java.util.stream.JAVA_STREAM; +import java.util.stream.StreamSupport; +#endif +import speiger.src.collections.PACKAGE.collections.SPLIT_ITERATOR; +import speiger.src.collections.PACKAGE.utils.SPLIT_ITERATORS; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.collections.utils.SanityChecks; + +#if TYPE_OBJECT +/** + * A Type-Specific Array-based implementation of list that is written to reduce (un)boxing + * + *

This implementation is optimized to improve how data is processed with interfaces like {@link ITrimmable}, {@link Stack} + * and with optimized functions that use type-specific implementations for primitives and optimized logic for bulkactions. + * + * @Type(T) + */ +public class COPY_ON_WRITE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE implements ITrimmable, Stack +#else +/** + * A Type-Specific Array-based implementation of list that is written to reduce (un)boxing + * + *

This implementation is optimized to improve how data is processed with interfaces like {@link ITrimmable}, {@link STACK} + * and with optimized functions that use type-specific implementations for primitives and optimized logic for bulkactions. + */ +public class COPY_ON_WRITE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE implements ITrimmable, STACK +#endif +{ + /** Access lock */ + transient ReentrantLock lock = new ReentrantLock(); + /** The backing array */ + protected transient KEY_TYPE[] data; + + /** + * Creates a new ArrayList with a Empty array. + */ + public COPY_ON_WRITE_LIST() { + data = EMPTY_KEY_ARRAY; + } + + /** + * Creates a new ArrayList a copy with the contents of the Collection. + * @param c the elements that should be added into the list + */ + @Deprecated + public COPY_ON_WRITE_LIST(Collection c) { + data = NEW_KEY_ARRAY(c.size()); + ITERATORS.unwrap(data, c.iterator()); + } + + /** + * Creates a new ArrayList a copy with the contents of the Collection. + * @param c the elements that should be added into the list + */ + public COPY_ON_WRITE_LIST(COLLECTION KEY_GENERIC_TYPE c) { + data = NEW_KEY_ARRAY(c.size()); + ITERATORS.unwrap(data, c.iterator()); + } + + /** + * Creates a new ArrayList a copy with the contents of the List. + * @param l the elements that should be added into the list + */ + public COPY_ON_WRITE_LIST(LIST KEY_GENERIC_TYPE l) { + data = NEW_KEY_ARRAY(l.size()); + l.getElements(0, data, 0, data.length); + } + + /** + * Creates a new ArrayList with a Copy of the array + * @param a the array that should be copied + */ + public COPY_ON_WRITE_LIST(KEY_TYPE... a) { + this(a, 0, a.length); + } + + /** + * Creates a new ArrayList with a Copy of the array with a custom length + * @param a the array that should be copied + * @param length the desired length that should be copied + */ + public COPY_ON_WRITE_LIST(KEY_TYPE[] a, int length) { + this(a, 0, length); + } + + /** + * Creates a new ArrayList with a Copy of the array with in the custom range. + * @param a the array that should be copied + * @param offset the starting offset of where the array should be copied from + * @param length the desired length that should be copied + * @throws IllegalStateException if offset is smaller then 0 + * @throws IllegalStateException if the offset + length exceeds the array length + */ + public COPY_ON_WRITE_LIST(KEY_TYPE[] a, int offset, int length) { + data = NEW_KEY_ARRAY(length); + SanityChecks.checkArrayCapacity(a.length, offset, length); + System.arraycopy(a, offset, data, 0, length); + } + +#if TYPE_OBJECT + /** + * Creates a new ArrayList with a EmptyObject array of the Type requested + * @param c the type of the array + * @Type(T) + * @return a typed List + */ + public static GENERIC_KEY_BRACES ARRAY_LIST KEY_GENERIC_TYPE of(Class c) { + ARRAY_LIST KEY_GENERIC_TYPE list = new ARRAY_LISTBRACES(); + list.data = (KEY_TYPE[])ObjectArrays.newArray(c, 0); + return list; + } + +#endif + + /** + * Appends the specified element to the end of this list. + * + * @param e element to be appended to this list + * @return true (as specified by {@link Collection#add}) + */ + @Override + public boolean add(KEY_TYPE e) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] newElements = Arrays.copyOf(data, data.length+1); + newElements[newElements.length-1] = e; + data = newElements; + } + finally { + lock.unlock(); + } + return true; + } + + /** + * Appends the specified element to the end of this Stack. + * @param e element to be appended to this Stack + */ + @Override + public void push(KEY_TYPE e) { + add(e); + } + + /** + * Appends the specified element to the index of the list + * @param index the index where to append the element to + * @param e the element to append to the list + * @throws IndexOutOfBoundsException if index is outside of the lists range + */ + @Override + public void add(int index, KEY_TYPE e) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + int size = data.length; + if(index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + KEY_TYPE[] newElements; + + if(index == size) { + newElements = Arrays.copyOf(data, size+1); + } + else { + newElements = NEW_KEY_ARRAY(size+1); + System.arraycopy(data, 0, newElements, 0, index); + System.arraycopy(data, index, newElements, index + 1, size - index); + } + newElements[index] = e; + this.data = newElements; + } + finally { + lock.unlock(); + } + } + + /** + * Appends the specified elements to the index of the list. + * This function may delegate to more appropriate function if necessary + * @param index the index where to append the elements to + * @param c the elements to append to the list + * @throws IndexOutOfBoundsException if index is outside of the lists range + * @throws NullPointerException if collection contains a null element + */ + @Override + @Deprecated + public boolean addAll(int index, Collection c) { + if(c instanceof COLLECTION) return addAll(index, (COLLECTION KEY_GENERIC_TYPE)c); + int add = c.size(); + if(add <= 0) return false; +#if !TYPE_OBJECT + if(c.contains(null)) throw new NullPointerException(); +#endif + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + int size = data.length; + if(index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + KEY_TYPE[] newElements; + if(index == size) { + newElements = Arrays.copyOf(data, size+add); + } + else { + newElements = NEW_KEY_ARRAY(size+add); + System.arraycopy(data, 0, newElements, 0, index); + System.arraycopy(data, index, newElements, index + add, size - index); + } + Iterator iter = c.iterator(); + while(add-- != 0) newElements[index++] = OBJ_TO_KEY(iter.next()); + this.data = newElements; + } + finally { + lock.unlock(); + } + return true; + } + + /** + * Appends the specified elements to the index of the list. + * This function may delegate to more appropriate function if necessary + * @param index the index where to append the elements to + * @param c the elements to append to the list + * @throws IndexOutOfBoundsException if index is outside of the lists range + */ + @Override + public boolean addAll(int index, COLLECTION KEY_GENERIC_TYPE c) { + if(c instanceof LIST) return addAll(index, (LIST KEY_GENERIC_TYPE)c); + int add = c.size(); + if(add <= 0) return false; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + int size = data.length; + if(index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + KEY_TYPE[] newElements; + if(index == size) { + newElements = Arrays.copyOf(data, size+add); + } + else { + newElements = NEW_KEY_ARRAY(size+add); + System.arraycopy(data, 0, newElements, 0, index); + System.arraycopy(data, index, newElements, index + add, size - index); + } + ITERATOR KEY_GENERIC_TYPE iter = c.iterator(); + while(add-- != 0) newElements[index++] = iter.NEXT(); + this.data = newElements; + } + finally { + lock.unlock(); + } + return true; + } + + /** + * Appends the specified elements to the index of the list. + * @param index the index where to append the elements to + * @param c the elements to append to the list + * @throws IndexOutOfBoundsException if index is outside of the lists range + */ + @Override + public boolean addAll(int index, LIST KEY_GENERIC_TYPE c) { + int add = c.size(); + if(add <= 0) return false; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + int size = data.length; + if(index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + KEY_TYPE[] newElements; + if(index == size) { + newElements = Arrays.copyOf(data, size+add); + } + else { + newElements = NEW_KEY_ARRAY(size+add); + System.arraycopy(data, 0, newElements, 0, index); + System.arraycopy(data, index, newElements, index + add, size - index); + } + c.getElements(0, newElements, index, c.size()); + this.data = newElements; + } + finally { + lock.unlock(); + } + return true; + } + + @Override + public boolean addAll(KEY_TYPE[] e, int offset, int length) { + if(length <= 0) return false; + SanityChecks.checkArrayCapacity(e.length, offset, length); + ReentrantLock lock = this.lock; + lock.lock(); + try { + int size = data.length; + KEY_TYPE[] newElements = Arrays.copyOf(data, size+length); + System.arraycopy(e, offset, newElements, size, length); + data = newElements; + } + finally { + lock.unlock(); + } + return true; + } + + /** + * Appends the specified array elements to the index of the list. + * @param from the index where to append the elements to + * @param a the elements to append to the list + * @param offset where to start ino the array + * @param length the amount of elements to insert + * @throws IndexOutOfBoundsException if index is outside of the lists range + */ + @Override + public void addElements(int from, KEY_TYPE[] a, int offset, int length) { + if(length <= 0) return; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + int size = data.length; + if(from < 0 || from > size) throw new IndexOutOfBoundsException("Index: " + from + ", Size: " + size); + KEY_TYPE[] newElements; + if(from == size) { + newElements = Arrays.copyOf(data, size+length); + } + else { + newElements = NEW_KEY_ARRAY(size+length); + System.arraycopy(data, 0, newElements, 0, from); + System.arraycopy(data, from, newElements, from + length, size - from); + } + System.arraycopy(a, offset, newElements, from, length); + this.data = newElements; + } + finally { + lock.unlock(); + } + } + + /** + * A function to fast fetch elements from the list + * @param from index where the list should be fetching elements from + * @param a the array where the values should be inserted to + * @param offset the startIndex of where the array should be written to + * @param length the number of elements the values should be fetched from + * @return the inputArray + * @throws NullPointerException if the array is null + * @throws IndexOutOfBoundsException if from is outside of the lists range + * @throws IllegalStateException if offset or length are smaller then 0 or exceed the array length + */ + @Override + public KEY_TYPE[] getElements(int from, KEY_TYPE[] a, int offset, int length) { + KEY_TYPE[] data = this.data; + SanityChecks.checkArrayCapacity(data.length, from, length); + SanityChecks.checkArrayCapacity(a.length, offset, length); + System.arraycopy(data, from, a, offset, length); + return a; + } + + /** + * a function to fast remove elements from the list. + * @param from the start index of where the elements should be removed from (inclusive) + * @param to the end index of where the elements should be removed to (exclusive) + */ + @Override + public void removeElements(int from, int to) { + int length = to - from; + if(length <= 0) return; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + if(to == data.length) this.data = Arrays.copyOf(data, data.length - length); + else { + int size = data.length-length; + KEY_TYPE[] newElements = NEW_KEY_ARRAY(size); + System.arraycopy(data, 0, newElements, 0, from); + System.arraycopy(data, to, newElements, from, data.length - to); + this.data = newElements; + } + } + finally { + lock.unlock(); + } + } + +#if TYPE_OBJECT + /** + * A function to fast extract elements out of the list, this removes the elements that were fetched. + * @param from the start index of where the elements should be fetched from (inclusive) + * @param to the end index of where the elements should be fetched to (exclusive) + * @param type the type of the OutputArray + * @return a array of the elements that were fetched + */ + @Override + public K[] extractElements(int from, int to, Class type) { + int length = to - from; + K[] a = ARRAYS.newArray(type, length); + if(length <= 0) return a; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + System.arraycopy(data, from, a, 0, length); + if(to == data.length) this.data = Arrays.copyOf(data, data.length - length); + else { + int size = data.length-1; + KEY_TYPE[] newElements = NEW_KEY_ARRAY(size); + System.arraycopy(data, 0, newElements, 0, from); + System.arraycopy(data, to, newElements, from, size - to); + this.data = newElements; + } + return a; + } + finally { + lock.unlock(); + } + } + +#else + /** + * A function to fast extract elements out of the list, this removes the elements that were fetched. + * @param from the start index of where the elements should be fetched from (inclusive) + * @param to the end index of where the elements should be fetched to (exclusive) + * @return a array of the elements that were fetched + */ + @Override + public KEY_TYPE[] extractElements(int from, int to) { + int length = to - from; + if(length <= 0) return EMPTY_KEY_ARRAY; + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] data = this.data; + KEY_TYPE[] a = NEW_KEY_ARRAY(length); + System.arraycopy(data, from, a, 0, length); + if(to == data.length) this.data = Arrays.copyOf(data, data.length - length); + else { + int size = data.length-1; + KEY_TYPE[] newElements = NEW_KEY_ARRAY(size); + System.arraycopy(data, 0, newElements, 0, from); + System.arraycopy(data, to, newElements, from, size - to); + this.data = newElements; + } + return a; + } + finally { + lock.unlock(); + } + } + +#endif +#if PRIMITIVES + @Override + public void fillBuffer(JAVA_BUFFER buffer) { + buffer.put(data, 0, data.length); + } + +#endif + /** + * A function to find if the Element is present in this list. + * @param o the element that is searched for + * @return if the element was found. + */ + @Override + @Deprecated + public boolean contains(Object o) { + return indexOf(o) != -1; + } + + /** + * A function to find the index of a given element + * @param o the element that is searched for + * @return the index of the element if found. (if not found then -1) + */ + @Override + @Deprecated + public int indexOf(Object o) { + KEY_TYPE[] data = this.data; +#if TYPE_OBJECT + if(o == null) { + for(int i = 0,m=data.length;i=0;i--) + if(data[i] == null) return i; + return -1; + } +#else + if(o == null) return -1; +#endif + for(int i = data.length - 1;i>=0;i--) { + if(Objects.equals(o, KEY_TO_OBJ(data[i]))) return i; + } + return -1; + } + +#if TYPE_OBJECT + /** + * Sorts the elements specified by the Natural order either by using the Comparator or the elements + * @param c the sorter of the elements, can be null + * @see java.util.List#sort(Comparator) + */ + @Override + public void sort(Comparator c) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] newData = Arrays.copyOf(data, data.length); + if(c != null) ARRAYS.stableSort(newData, newData.length, c); + else ARRAYS.stableSort(newData, newData.length); + data = newData; + } + finally { + lock.unlock(); + } + } + + /** + * Sorts the elements specified by the Natural order either by using the Comparator or the elements using a unstable sort + * @param c the sorter of the elements, can be null + * @see java.util.List#sort(Comparator) + */ + @Override + public void unstableSort(Comparator c) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] newData = Arrays.copyOf(data, data.length); + if(c != null) ARRAYS.unstableSort(newData, newData.length, c); + else ARRAYS.unstableSort(newData, newData.length); + data = newData; + } + finally { + lock.unlock(); + } + } + +#else + /** + * A Type Specific implementation of the Collection#contains function. + * @param e the element that is searched for. + * @return if the element was found + */ + @Override + public boolean contains(KEY_TYPE e) { + return indexOf(e) != -1; + } + + /** + * A Type-Specific function to find the index of a given element + * @param e the element that is searched for + * @return the index of the element if found. (if not found then -1) + */ + @Override + public int indexOf(KEY_TYPE e) { + KEY_TYPE[] data = this.data; + for(int i = 0,m=data.length;i=0;i--) { + if(KEY_EQUALS(data[i], e)) return i; + } + return -1; + } + + /** + * Sorts the elements specified by the Natural order either by using the Comparator or the elements + * @param c the sorter of the elements, can be null + * @see java.util.List#sort(java.util.Comparator) + * @see ARRAYS#stableSort(KEY_TYPE[], COMPARATOR) + */ + @Override + public void sort(COMPARATOR c) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] newData = Arrays.copyOf(data, data.length); + if(c != null) ARRAYS.stableSort(newData, newData.length, c); + else ARRAYS.stableSort(newData, newData.length); + data = newData; + } + finally { + lock.unlock(); + } + } + + /** + * Sorts the elements specified by the Natural order either by using the Comparator or the elements using a unstable sort + * @param c the sorter of the elements, can be null + * @see java.util.List#sort(java.util.Comparator) + * @see ARRAYS#unstableSort(KEY_TYPE[], COMPARATOR) + */ + @Override + public void unstableSort(COMPARATOR c) { + ReentrantLock lock = this.lock; + lock.lock(); + try { + KEY_TYPE[] newData = Arrays.copyOf(data, data.length); + if(c != null) ARRAYS.unstableSort(newData, newData.length, c); + else ARRAYS.unstableSort(newData, newData.length); + data = newData; + } + finally { + lock.unlock(); + } + } + +#endif + /** + * A Type-Specific get function to reduce (un)boxing + * @param index the index of the element to fetch + * @return the value of the requested index + * @throws IndexOutOfBoundsException if the index is out of range + */ + @Override + public KEY_TYPE GET_KEY(int index) { + checkRange(index); + return data[index]; + } + + /** + * Provides the Selected Object from the stack. + * Top to bottom + * @param index of the element that should be provided + * @return the element that was requested + * @throws ArrayIndexOutOfBoundsException if the index is out of bounds + * @see speiger.src.collections.utils.Stack#peek(int) + */ + @Override + public KEY_TYPE peek(int index) { + checkRange((size() - 1) - index); + return data[(size() - 1) - index]; + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) { + if(index < 0 || index > size()) throw new IndexOutOfBoundsException(); + return new COWIteratorBRACES(data, index); + } + + @Override + public LIST KEY_GENERIC_TYPE subList(int fromIndex, int toIndex) { + SanityChecks.checkArrayCapacity(data.length, fromIndex, toIndex-fromIndex); + return new COWSubListBRACES(this, 0, fromIndex, toIndex); + } + + /** + * A Type Specific foreach function that reduces (un)boxing + * + * @implSpec + *

The default implementation behaves as if: + *

{@code
+	 * 	for(int i = 0,m=data.length;i
+	 *
+	 * @param action The action to be performed for each element
+	 * @throws NullPointerException if the specified action is null
+	 * @see Iterable#forEach(java.util.function.Consumer)
+	 */
+	@Override
+	public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) {
+		Objects.requireNonNull(action);
+		KEY_TYPE[] data = this.data;
+		for(int i = 0,m=data.length;i void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) {
+		Objects.requireNonNull(action);
+		KEY_TYPE[] data = this.data;
+		for(int i = 0,m=data.length;i KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) {
+		Objects.requireNonNull(operator);
+		KEY_TYPE[] data = this.data;
+		KEY_SPECIAL_TYPE state = identity;
+		for(int i = 0,m=data.length;i o) {
+#if PRIMITIVES
+		Objects.requireNonNull(o);
+#if TYPE_BYTE || TYPE_SHORT || TYPE_CHAR || TYPE_FLOAT
+		REPLACE(T -> OBJ_TO_KEY(o.apply(KEY_TO_OBJ(SanityChecks.SANITY_CAST(T)))));
+#else
+		REPLACE(T -> OBJ_TO_KEY(o.apply(KEY_TO_OBJ(T))));
+#endif
+#else
+		Objects.requireNonNull(o);
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			KEY_TYPE[] newData = Arrays.copyOf(data, data.length);
+			for(int i = 0,m=newData.length;i c) {
+		if(c.isEmpty()) return false;
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			KEY_TYPE[] data = this.data;
+			KEY_TYPE[] newElements = NEW_KEY_ARRAY(data.length);
+			int j = 0;
+			for(int i= 0,m=data.length;i c) {
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			KEY_TYPE[] data = this.data;
+			if(c.isEmpty()) {
+				if(data.length > 0) {
+					this.data = EMPTY_KEY_ARRAY;
+					return true;
+				}
+				return false;
+			}
+			KEY_TYPE[] newElements = NEW_KEY_ARRAY(data.length);
+			int j = 0;
+			for(int i= 0,m=data.length;i filter) {
+		Objects.requireNonNull(filter);
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			KEY_TYPE[] data = this.data;
+			KEY_TYPE[] newElements = NEW_KEY_ARRAY(data.length);
+			int j = 0;
+			for(int i= 0,m=data.length;i 0) {
+					this.data = EMPTY_KEY_ARRAY;
+					return true;
+				}
+				return false;
+			}
+			KEY_TYPE[] newElements = NEW_KEY_ARRAY(data.length);
+			int j = 0;
+			for(int i= 0,m=data.length;i 0) {
+					forEach(r);
+					this.data = EMPTY_KEY_ARRAY;
+					return true;
+				}
+				return false;
+			}
+			KEY_TYPE[] newElements = NEW_KEY_ARRAY(data.length);
+			int j = 0;
+			for(int i= 0,m=data.length;i E[] toArray(E[] a) {
+		KEY_TYPE[] data = this.data;
+		int size = data.length;
+		if(a == null) a = (E[])new Object[size];
+		else if(a.length < size) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size);
+#if TYPE_OBJECT
+        System.arraycopy(data, 0, a, 0, size);
+#else
+		for(int i = 0;i size) a[size] = null;
+		return a;
+	}
+	
+#if !TYPE_OBJECT
+	@Override
+	public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) {
+		KEY_TYPE[] data = this.data;
+		int size = data.length;
+		if(a.length < size) a = new KEY_TYPE[size];
+		System.arraycopy(data, 0, a, 0, size);
+		if (a.length > size) a[size] = EMPTY_KEY_VALUE;
+		return a;
+	}
+	
+#endif
+	
+	/**
+	 * A function to return the size of the list
+	 * @return the size of elements in the list
+	 */
+	@Override
+	public int size() {
+		return data.length;
+	}
+	
+	/**
+	 * A function to ensure the elements are within the requested size.
+	 * If smaller then the stored elements they get removed as needed.
+	 * If bigger it is ensured that enough room is provided depending on the implementation
+	 * @param size the requested amount of elements/room for elements
+	 */
+	@Override
+	public void size(int size) {
+		if(size == data.length || size < 0) return;
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			data = Arrays.copyOf(data, size);
+		}
+		finally {
+			lock.unlock();
+		}
+	}
+	
+	/**
+	 * A function to clear all elements in the list.
+	 */
+	@Override
+	public void clear() {
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			data = EMPTY_KEY_ARRAY;
+		}
+		finally {
+			lock.unlock();
+		}
+	}
+	
+	/**
+	 * Trims the original collection down to the size of the current elements or the requested size depending which is bigger
+	 * @param size the requested trim size.
+	 */
+	@Override
+	public boolean trim(int size) {
+		return false;
+	}
+	
+	/**
+	 * Trims the collection down to the requested size and clears all elements while doing so
+	 * @param size the amount of elements that should be allowed
+	 * @note this will enforce minimum size of the collection itself
+	 */
+	@Override
+	public void clearAndTrim(int size) {
+		ReentrantLock lock = this.lock;
+		lock.lock();
+		try {
+			data = EMPTY_KEY_ARRAY;
+		}
+		finally {
+			lock.unlock();
+		}
+	}
+	
+	@Override
+	public COPY_ON_WRITE_LIST KEY_GENERIC_TYPE copy() {
+		COPY_ON_WRITE_LIST KEY_GENERIC_TYPE list = new COPY_ON_WRITE_LISTBRACES();
+		list.data = Arrays.copyOf(data, data.length);
+		return list;
+	}
+	
+	protected void checkRange(int index) {
+		if (index < 0 || index >= data.length)
+			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + data.length);
+	}
+	
+	#if PRIMITIVES
+	/**
+	 * Returns a Java-Type-Specific Stream to reduce boxing/unboxing.
+	 * @return a Stream of the closest java type
+	 * @note characteristics are ordered, sized, subsized
+	 */
+	@Override
+	public JAVA_STREAM primitiveStream() { return StreamSupport.NEW_STREAM(SPLIT_ITERATORS.createArrayJavaSplititerator(data, data.length, 16464), false); }
+	
+#endif
+	/**
+	 * A Type Specific Type Splititerator to reduce boxing/unboxing
+	 * @return type specific splititerator
+	 * @note characteristics are ordered, sized, subsized
+	 */
+	@Override
+	public SPLIT_ITERATOR KEY_GENERIC_TYPE spliterator() { return SPLIT_ITERATORS.createArraySplititerator(data, data.length, 16464); }
+	
+	static final class COWIterator KEY_GENERIC_TYPE implements LIST_ITERATOR KEY_GENERIC_TYPE
+	{
+		KEY_TYPE[] data;
+		int index;
+		
+		public COWIterator(KEY_TYPE[] data, int index) {
+			this.data = data;
+			this.index = index;
+		}
+		
+		@Override
+		public boolean hasNext() {
+			return index < data.length;
+		}
+		
+		@Override
+		public KEY_TYPE NEXT() {
+			if(!hasNext()) throw new NoSuchElementException();
+			return data[index++];
+		}
+		
+		@Override
+		public boolean hasPrevious() {
+			return index > 0;
+		}
+		
+		@Override
+		public KEY_TYPE PREVIOUS() {
+			if(!hasPrevious()) throw new NoSuchElementException();
+			return data[index--];
+		}
+		
+		@Override
+		public int nextIndex() {
+			return index;
+		}
+		
+		@Override
+		public int previousIndex() {
+			return index-1;
+		}
+		
+		@Override
+		public void remove() { throw new UnsupportedOperationException(); }
+		@Override
+		public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); }
+		@Override
+		public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); }
+		
+		@Override
+		public int skip(int amount) {
+			if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed");
+			int steps = Math.min(amount, (data.length - 1) - index);
+			index += steps;
+			return steps;
+		}
+		
+		@Override
+		public int back(int amount) {
+			if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed");
+			int steps = Math.min(amount, index);
+			index -= steps;
+			return steps;
+		}
+	}
+	
+	private static class COWSubList KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE
+	{
+		final COPY_ON_WRITE_LIST KEY_GENERIC_TYPE list;
+		final int parentOffset;
+		final int offset;
+		int size;
+		
+		public COWSubList(COPY_ON_WRITE_LIST KEY_GENERIC_TYPE list, int offset, int from, int to) {
+			this.list = list;
+			this.parentOffset = from;
+			this.offset = offset + from;
+			this.size = to - from;
+		}
+		
+		@Override
+		public void add(int index, KEY_TYPE element) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkAddSubRange(index);
+				list.add(parentOffset+index, element);
+				size++;				
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public boolean addAll(int index, Collection c) {
+			int add = c.size();
+			if(add <= 0) return false;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkAddSubRange(index);
+				list.addAll(parentOffset+index, c);
+				this.size += add;
+			}
+			finally {
+				lock.unlock();
+			}
+			return true;
+		}
+		
+		@Override
+		public boolean addAll(int index, COLLECTION KEY_GENERIC_TYPE c) {
+			int add = c.size();
+			if(add <= 0) return false;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkAddSubRange(index);
+				list.addAll(parentOffset+index, c);
+				this.size += add;
+			}
+			finally {
+				lock.unlock();
+			}
+			return true;
+		}
+
+		@Override
+		public boolean addAll(int index, LIST KEY_GENERIC_TYPE c) {
+			int add = c.size();
+			if(add <= 0) return false;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkAddSubRange(index);
+				list.addAll(parentOffset+index, c);
+				this.size += add;
+			}
+			finally {
+				lock.unlock();
+			}
+			return true;
+		}
+		
+		@Override
+		public void addElements(int from, KEY_TYPE[] a, int offset, int length) {
+			if(length <= 0) return;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkAddSubRange(from);
+				list.addElements(parentOffset+from, a, offset, length);
+				this.size += length;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public KEY_TYPE[] getElements(int from, KEY_TYPE[] a, int offset, int length) {
+			SanityChecks.checkArrayCapacity(size, from, length);
+			SanityChecks.checkArrayCapacity(a.length, offset, length);
+			return list.getElements(from+this.offset, a, offset, length);
+		}
+		
+		@Override
+		public void removeElements(int from, int to) {
+			if(to-from <= 0) return;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(from);
+				checkSubRange(to);
+				list.removeElements(from+parentOffset, to+parentOffset);
+				size -= to - from;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+#if TYPE_OBJECT
+		@Override
+		public  K[] extractElements(int from, int to, Class type) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(from);
+				checkSubRange(to);
+				K[] result = list.extractElements(from+parentOffset, to+parentOffset, type);
+				size -= to - from;
+				return result;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+
+#else
+		@Override
+		public KEY_TYPE[] extractElements(int from, int to) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(from);
+				checkSubRange(to);
+				KEY_TYPE[] result = list.extractElements(from+parentOffset, to+parentOffset);
+				size -= to - from;
+				return result;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+#endif
+		@Override
+		public KEY_TYPE GET_KEY(int index) {
+			checkSubRange(index);
+			return list.GET_KEY(offset+index);
+		}
+
+		@Override
+		public KEY_TYPE set(int index, KEY_TYPE element) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(index);
+				return list.set(offset+index, element);
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public KEY_TYPE swapRemove(int index) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(index);
+				KEY_TYPE result = list.swapRemove(index+parentOffset);
+				size--;
+				return result;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public KEY_TYPE REMOVE(int index) {
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				checkSubRange(index);
+				KEY_TYPE result = list.REMOVE(index+parentOffset);
+				size--;
+				return result;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public int size() {
+			return size;
+		}
+		
+		@Override
+		public void clear() {
+			if(size == 0) return;
+			ReentrantLock lock = list.lock;
+			lock.lock();
+			try {
+				list.removeElements(offset, offset+size);
+				size = 0;
+			}
+			finally {
+				lock.unlock();
+			}
+		}
+		
+		@Override
+		public SPLIT_ITERATOR KEY_GENERIC_TYPE spliterator() { return SPLIT_ITERATORS.createSplititerator(this, 16464); }
+		
+		@Override
+		public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) {
+			if(index < 0 || index > size()) throw new IndexOutOfBoundsException();
+			return new COWSubListIteratorBRACES(this, index);
+		}
+		
+		@Override
+		public LIST KEY_GENERIC_TYPE subList(int fromIndex, int toIndex) {
+			SanityChecks.checkArrayCapacity(size, fromIndex, toIndex-fromIndex);
+			return new COWSubListBRACES(list, offset, fromIndex, toIndex);
+		}
+		
+		protected void checkSubRange(int index) {
+			if (index < 0 || index >= size)
+				throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+		}
+		
+		protected void checkAddSubRange(int index) {
+			if (index < 0 || index > size)
+				throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+		}
+	}
+	
+	private static class COWSubListIterator KEY_GENERIC_TYPE implements LIST_ITERATOR KEY_GENERIC_TYPE
+	{
+		ABSTRACT_LIST KEY_GENERIC_TYPE list;
+		int index;
+		
+		COWSubListIterator(ABSTRACT_LIST KEY_GENERIC_TYPE list, int index) {
+			this.list = list;
+			this.index = index;
+		}
+		
+		@Override
+		public boolean hasNext() {
+			return index < list.size();
+		}
+		
+		@Override
+		public KEY_TYPE NEXT() {
+			if(!hasNext()) throw new NoSuchElementException();
+			return list.GET_KEY(index++);
+		}
+		
+		@Override
+		public boolean hasPrevious() {
+			return index > 0;
+		}
+		
+		@Override
+		public KEY_TYPE PREVIOUS() {
+			if(!hasPrevious()) throw new NoSuchElementException();
+			return list.GET_KEY(--index);
+		}
+		
+		@Override
+		public int nextIndex() {
+			return index;
+		}
+		
+		@Override
+		public int previousIndex() {
+			return index-1;
+		}
+		
+		@Override
+		public void remove() { throw new UnsupportedOperationException(); }
+		
+		@Override
+		public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); }
+		
+		@Override
+		public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); }
+		
+		@Override
+		public int skip(int amount) {
+			if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed");
+			int steps = Math.min(amount, (list.size() - 1) - index);
+			index += steps;
+			return steps;
+		}
+		
+		@Override
+		public int back(int amount) {
+			if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed");
+			int steps = Math.min(amount, index);
+			index -= steps;
+			return steps;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java b/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java
index 4fafa0e1..eaf57e3e 100644
--- a/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java
+++ b/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java
@@ -1,5 +1,8 @@
 package speiger.src.collections.objects.list;
 
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Function;
 
@@ -8,14 +11,19 @@ import com.google.common.collect.testing.TestStringListGenerator;
 import com.google.common.collect.testing.features.CollectionFeature;
 import com.google.common.collect.testing.features.CollectionSize;
 import com.google.common.collect.testing.features.ListFeature;
+import com.google.common.collect.testing.testers.CollectionSpliteratorTester;
+import com.google.common.collect.testing.testers.ListListIteratorTester;
+import com.google.common.collect.testing.testers.ListSubListTester;
 
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import speiger.src.collections.ints.lists.CopyOnWriteIntArrayList;
 import speiger.src.collections.ints.lists.ImmutableIntList;
 import speiger.src.collections.ints.lists.IntArrayList;
 import speiger.src.collections.ints.lists.IntLinkedList;
 import speiger.src.collections.ints.lists.IntList;
+import speiger.src.collections.objects.lists.CopyOnWriteObjectArrayList;
 import speiger.src.collections.objects.lists.ImmutableObjectList;
 import speiger.src.collections.objects.lists.ObjectArrayList;
 import speiger.src.collections.objects.lists.ObjectLinkedList;
@@ -28,9 +36,11 @@ public class ObjectListTests extends TestCase
 		TestSuite suite = new TestSuite("Lists");
 		suite.addTest(suite("ArrayList", T -> new ObjectArrayList<>(T)));
 		suite.addTest(suite("LinkedList", T -> new ObjectLinkedList<>(T)));
+		suite.addTest(copyOnWriteSuite("CopyOnWriteArrayList", T -> new CopyOnWriteObjectArrayList<>(T)));
 		suite.addTest(immutableSuite("ImmutableList", T -> new ImmutableObjectList<>(T)));
 		suite.addTest(intSuite("IntArrayList", IntArrayList::new));
 		suite.addTest(intSuite("IntLinkedList", IntLinkedList::new));
+		suite.addTest(intCopyOnWriteSuite("CopyOnWriteArrayList", CopyOnWriteIntArrayList::new));
 		suite.addTest(intImmutableSuite("IntImmutableList", ImmutableIntList::new));
 		return suite;
 	}
@@ -39,6 +49,10 @@ public class ObjectListTests extends TestCase
 		return ListTestSuiteBuilder.using(new TestIntListGenerator(factory)).named(name).withFeatures(ListFeature.GENERAL_PURPOSE, CollectionSize.ANY).createTestSuite();
 	}
 	
+	public static Test intCopyOnWriteSuite(String name, Function factory) {
+		return ListTestSuiteBuilder.using(new TestIntListGenerator(factory)).named(name).suppressing(suppressForCopyOnWriteArrayList()).withFeatures(ListFeature.SUPPORTS_SET, ListFeature.SUPPORTS_ADD_WITH_INDEX, ListFeature.SUPPORTS_REMOVE_WITH_INDEX, CollectionFeature.SUPPORTS_ADD, CollectionFeature.SUPPORTS_REMOVE, CollectionSize.ANY).createTestSuite();
+	}
+	
 	public static Test intImmutableSuite(String name, Function factory) {
 		return ListTestSuiteBuilder.using(new TestIntListGenerator(factory)).named(name).withFeatures(CollectionSize.ANY).createTestSuite();
 	}
@@ -50,10 +64,22 @@ public class ObjectListTests extends TestCase
 		}).named(name).withFeatures(ListFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite();
 	}
 	
+	public static Test copyOnWriteSuite(String name, Function> factory) {
+		return ListTestSuiteBuilder.using(new TestStringListGenerator() {
+			@Override
+			protected List create(String[] elements) { return factory.apply(elements); }
+		}).named(name).suppressing(suppressForCopyOnWriteArrayList()).withFeatures(ListFeature.SUPPORTS_SET, ListFeature.SUPPORTS_ADD_WITH_INDEX, ListFeature.SUPPORTS_REMOVE_WITH_INDEX, CollectionFeature.SUPPORTS_ADD, CollectionFeature.SUPPORTS_REMOVE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite();
+	}
+	
 	public static Test immutableSuite(String name, Function> factory) {
 		return ListTestSuiteBuilder.using(new TestStringListGenerator() {
 			@Override
 			protected List create(String[] elements) { return factory.apply(elements); }
 		}).named(name).withFeatures(CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite();
 	}
+	
+	public static Collection suppressForCopyOnWriteArrayList()
+	{
+		return Arrays.asList(ListSubListTester.getSubListOriginalListSetAffectsSubListMethod(), ListSubListTester.getSubListOriginalListSetAffectsSubListLargeListMethod(), ListSubListTester.getSubListSubListRemoveAffectsOriginalLargeListMethod(), ListListIteratorTester.getListIteratorFullyModifiableMethod(), CollectionSpliteratorTester.getSpliteratorNotImmutableCollectionAllowsAddMethod(), CollectionSpliteratorTester.getSpliteratorNotImmutableCollectionAllowsRemoveMethod());
+	}
 }