Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
* This implementation is NOT synchronized and thus should only be used by a
* single thread or in a thread-safe manner (i.e. read-only as soon as multiple threads access it).
* Performance gain compared to Sun's JDK ArrayList implementation is around 10% to 20%
* (memory use is 33% more in worst case, 50% less in best case, slightly more in average case)
*
* Note that this List implementation does NOT keep track of modification count as Sun's collection implementations do
* (and thus never throws a {@link ConcurrentModificationException}), for two reasons:
* 1.) It is already explicitely declared thread-unsafe and for single-thread (or thread-safe)
* use only.
* 2.) The common modCount-concurrency exception behaviour ("failfast") throws {@link ConcurrentModificationException}
* even in single thread use, i.e. when iterating over a collection and removing more than one element of it
* without using the iterator's method.
*
* Current conclusion is that the JDK's failfast implementations buy unneeded (and even unreliable as stated by
* official guides) currency modification recognition at the cost of performance loss and even a bug when already used
* safely.
* If this kind of concurrent modification recognition is needed, it is recommended to use {@link java.util.ArrayList}.
*
* Additionally, this class implements the extended List {@link XList} interface, thus providing higher order
* functionality like {@link #execute(Operation)} or {@link #reduce(Predicate)}.
*
* @author Thomas Muenz
*/
public final class GrowList implements XList
{
///////////////////////////////////////////////////////////////////////////
// constants //
/////////////////////
// have to be 2^n values
static final int MINIMUM_DATA_SIZE = 8;
static final int DEFAULT_DATA_SIZE = 32; //array allocation takes about the same time up to a size of 32
static final int MAXIMUM_POW2_SIZE = 1<<30; //the highest possible int value that can be reached by bit shifting
// internal marker object for marking to be removed bucckets for batch removal
private static final Object REMOVE_MARKER = new Object();
///////////////////////////////////////////////////////////////////////////
// static methods //
/////////////////////
private static final int calcCapacity(final int n)
{
if(n > MAXIMUM_POW2_SIZE){
return Integer.MAX_VALUE; //cannot be reached by shifting
}
int p2 = MINIMUM_DATA_SIZE;
while(p2 < n){
p2 <<= 1;
}
return p2;
}
///////////////////////////////////////////////////////////////////////////
// instance fields //
////////////////////
private Object[] data;
private int size;
///////////////////////////////////////////////////////////////////////////
// constructors //
/////////////////
/**
* Instantiates a new {@link GrowList} object with the default capacity.
*/
public GrowList()
{
super();
this.data = new Object[DEFAULT_DATA_SIZE];
this.size = 0;
}
/**
* Instantiates a new {@link GrowList} object with a given minimal capacity.
* Note that the actual capacity will propably be higher, as capacity ist always padded to be a 2^n value.
*
* @param minimalCapacity the desired minimal capacity
*/
public GrowList(final int minimalCapacity)
{
super();
// will default to MINIMUM_DATA_SIZE and apart from that: is indicating those errors really task of this class?
// if(initialCapacity < 0){
// throw new IllegalArgumentException("initial capacity may not be negative: "+initialCapacity);
// }
this.data = new Object[calcCapacity(minimalCapacity)];
this.size = 0;
}
/**
*
* @param original
* @throws NullPointerException
*/
public GrowList(final GrowList extends E> original) throws NullPointerException
{
super();
this.size = original.size;
this.data = new Object[original.data.length];
System.arraycopy(original.data, 0, this.data, 0, original.size);
}
/**
*
* @param collection
* @throws NullPointerException
*/
public GrowList(final Collection extends E> collection) throws NullPointerException
{
this(collection.size());
this.addAll(collection);
}
/**
*
* @param elements
* @throws NullPointerException
*/
public GrowList(final E... elements) throws NullPointerException
{
super();
this.size = elements.length;
this.data = new Object[calcCapacity(elements.length)];
System.arraycopy(elements, 0, this.data, 0, this.size);
}
/**
* Note that the passed array MUST have a 2^n length. Otherwise the list will crash/fail/whatever
*
* @param internalData
*/
GrowList(final Object[] internalData, final int size)
{
super();
this.data = internalData;
this.size = size;
}
///////////////////////////////////////////////////////////////////////////
// getters //
/////////////////////
/**
* @return the data
*/
Object[] getData()
{
return this.data;
}
/**
* @return the size
*/
int getSize()
{
return this.size;
}
///////////////////////////////////////////////////////////////////////////
// setters //
/////////////////////
/**
* @param size the size to set
*/
void setSize(final int size)
{
this.size = size;
}
/**
*
* @param data
*/
void setData(final Object[] data)
{
this.data = data;
}
/**
*
* @param data
* @param size
*/
void setContent(final Object[] data, final int size)
{
this.data = data;
this.size = size;
}
///////////////////////////////////////////////////////////////////////////
// declared methods //
/////////////////////
private int calculateCapacityIncrease(final int increase)
{
if(Integer.MAX_VALUE - increase < this.size){
throw new ArrayIndexOutOfBoundsException(this.exceptionStringArrayLengthExceeded(increase));
}
final int newSize = this.size + increase;
if(newSize > MAXIMUM_POW2_SIZE){
return Integer.MAX_VALUE; //cannot be reached by shifting
}
int c = this.data.length;
while(c < newSize){
c <<= 1;
}
return c;
}
private void internalSwap(final int i, final int j)
{
final Object[] data = this.data; //cache variable really helps, mostly for writing accesses
final Object t = data[i];
data[i] = data[j];
data[j] = t;
}
private void internalAdd(final Object e)
{
if(this.size == this.data.length){
//expandArray()
final int length = this.data.length;
final Object[] data = new Object[length == MAXIMUM_POW2_SIZE ?Integer.MAX_VALUE : length<<1];
System.arraycopy(this.data, 0, data, 0, length);
this.data = data;
}
this.data[this.size++] = e;
}
private void internalInsertArray(final int index, final Object[] elements, final int elementsSize)
{
Object[] data = this.data;
final int targetCapacity = this.calculateCapacityIncrease(elementsSize);
if(targetCapacity > data.length){
data = new Object[targetCapacity];
/* copy elements in two steps:
* 1.) [ 0; index] -> [ 0; index]
* 2.) [index; size ] -> [index+space; size+space]
*
* So it looks like this:
* ----1.)---- ----2.)----
* |||||||||||_______|||||||||||
* where this ^^^^^^^ is exactely enough space for inserting "elements"
*
* this way, all to be moved elements are only copied once
*/
System.arraycopy(this.data, 0, data, 0, index);
System.arraycopy(this.data, index, data, index + elementsSize, this.size - index);
this.data = data;
}
else {
// free up enough space at index
System.arraycopy(data, index, data, index + elementsSize, this.size - index);
}
System.arraycopy(elements, 0, data, index, elementsSize);
this.size += elementsSize;
}
private String exceptionStringRange(final int startIndex, final int endIndex)
{
return "Range ["+endIndex+';'+startIndex+"] not in [0;"+(this.size-1)+"]";
}
private String exceptionStringSkipNegative(final int skip)
{
return "Skip count may not be negative: "+skip;
}
private String exceptionStringIndexOutOfBounds(final int index)
{
return "Index: "+index+", Size: "+this.size;
}
private String exceptionStringArrayLengthExceeded(final int desiredIncrease)
{
return "Required array size (" + ((long)desiredIncrease + this.size) + ") exceeds maximum java array size";
}
private String exceptionStringIterableOffsetNegative(final int srcOffset)
{
return "Iterable offset count may not be negative: "+srcOffset;
}
private String exceptionStringIterableLengthNegative(final int length)
{
return "Iterable length count may not be negative: "+length;
}
private String exceptionStringIllegalSwapBounds(final int indexA, final int indexB, final int length)
{
return "Illegal swap bounds: ("+indexA+"["+length+"] -> "+indexB+"["+length+"]) in range [0;"+this.size+"]";
}
/**
* Insertionsort is faster than Quick- or Mergesort for very small arrays.
* Even though the implementations for those two more complex algorithms use a built-in Insertionsort for small
* lists (or sub lists) below a certain threshold length, it can still be faster for a GrowList to be sorted
* by Insertionsort explictely for the following reasons:
*
*
No threshold check has to be performed at all.
*
Threshold for built-in Insertionsort decision is hardcoded, but depends on hardware,
* meaning future hardware can achieve better results with Insertionsort for higher thresholds.
*
No method call and argument passing (array, startIndex, endIndex) needed, instead hardcoded bounds in
* Insertionsort's loop
*
*/
@SuppressWarnings("unchecked")
public void sortInsertion(final Comparator comparator)
{
final Object[] data = this.data;
for(int i = 0; i <= this.size; i++){
for(int j = i; j != 0 && comparator.compare((E)data[j-1], (E)data[j]) > 0; j--){
final Object t = data[j];
data[j] = data[j-1];
data[j-1] = t;
}
}
}
/**
*
* @param c
* @return
*/
public boolean addAll(final GrowList extends E> c)
{
if(c.size == 0) return false;
final int targetCapacity = this.calculateCapacityIncrease(c.size);
if(targetCapacity > this.data.length){
final Object[] data = new Object[targetCapacity];
System.arraycopy(this.data, 0, data, 0, this.size);
this.data = data;
}
System.arraycopy(c.data, 0, this.data, this.size, c.size);
this.size += c.size;
return true;
}
/**
*
* @param index
* @param c
* @return
*/
public boolean addAll(final int index, final GrowList extends E> c)
{
if(index > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
if(index < 0){
throw new ArrayIndexOutOfBoundsException(index);
}
if(c.size == 0) return false;
if(index == this.size){
return this.addAll(c);
}
this.internalInsertArray(index, c.data, c.size);
return true;
}
/**
*
* @return
*/
@SuppressWarnings("unchecked")
public ArrayList toArrayList()
{
// include no buffer here as this conversion will mostly be needed for reading the existing data
final ArrayList al = new ArrayList(this.size);
// intentionally don't use ArrayList.addAll() here as it creates an additional array copy.
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
al.add((E)data[i]); //will never have to extend the elementData array as initialCapacity fits size.
}
return al;
}
/**
* Shrinks the buckets to the smallest possible size.
* Note that this may not be the same as the element count due to implementation details
*/
public GrowList shrink()
{
final int desiredDataLength = calcCapacity(this.size);
if(desiredDataLength == this.data.length) return this;
//expandArray() (sort of)
final Object[] data = new Object[desiredDataLength];
if(this.size > 0){
System.arraycopy(this.data, 0, data, 0, this.size);
}
this.data = data;
return this;
}
/**
*
* @param minCapacity
* @return
*/
public GrowList ensureCapacity(final int minCapacity)
{
if(minCapacity <= this.data.length) return this;
final Object[] data = new Object[calcCapacity(minCapacity)];
System.arraycopy(this.data, 0, data, 0, this.size);
this.data = data;
return this;
}
/**
*
* @return
*/
@SuppressWarnings("unchecked")
public VarList toVarList()
{
final VarList vl = new VarList(this.size);
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
vl.add((E)data[i]);
}
return vl;
}
///////////////////////////////////////////////////////////////////////////
// override methods //
/////////////////////
/**
* @param obj
* @return
*/
@Deprecated
@Override
public boolean equals(final Object o)
{
//trivial escape conditions
if(o == this) return true;
if(o == null || !(o instanceof List>)) return false;
final List> list = (List>)o;
if(this.size != list.size()) return false; //lists can only be equal if they have the same length
final Object[] data = this.data;
int i = 0;
for(final Object e2 : list) { //use iterator for passed list as it could be a non-random-access list
final Object e1 = data[i++];
if(e1 == null){ //null-handling escape conditions
if(e1 != null) return false;
continue;
}
if(!e1.equals(e2)) return false;
}
return true; //no un-equal element found, so lists must be equal
}
/**
* @return
*/
@Deprecated
@Override
public int hashCode()
{
final Object[] data = this.data;
int hashCode = 1;
for(int i = 0, size = this.size; i < size; i++){
final Object obj = data[i];
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); //copied from java.util.ArrayList
}
return hashCode;
}
/**
* Returns a string representation of this list. The string
* representation consists of a list of the collection's elements in the
* order they are contained in this list, enclosed in square brackets
* ("[]"). Adjacent elements are separated by the characters
* ", " (comma and space). Elements are converted to strings as
* by {@link String#valueOf(Object)}.
*
* If an element is this list itself, then the string produced by {@link Object#toString()} for this list
* is used as its elemental string representation.
*
* @return a string representation of this collection
*/
@Override
public String toString()
{
final int size = this.size;
if(size == 0){
// array causes problems with escape condition otherwise
return "[]";
}
final VarChar vc = new VarChar(size >= 1<<28 ?Integer.MAX_VALUE :size<<3).append('[');
final Object[] data = this.data;
Object e;
for(int i = 0; i < size; i++){
e = data[i];
vc.append(e == this ?super.toString() :e).append(',', ' ');
}
vc.deleteLastChar();
vc.setLastChar(']');
return vc.toString();
}
/**
* @param e
* @return
*/
@Override
public boolean add(final E e)
{
if(this.size == this.data.length){
//expandArray()
final int length = this.data.length;
final Object[] data = new Object[length == MAXIMUM_POW2_SIZE ?Integer.MAX_VALUE : length<<1];
System.arraycopy(this.data, 0, data, 0, length);
this.data = data;
}
this.data[this.size++] = e;
return true;
}
/**
* @param index
* @param element
*/
@Override
public void add(final int index, final E element) throws IndexOutOfBoundsException
{
if(index >= this.size || index < 0){
if(index == this.size){
this.add(element);
return;
}
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
Object[] data = this.data;
if(this.size == data.length){
//expandArray()
final int length = this.data.length;
data = new Object[length == MAXIMUM_POW2_SIZE ?Integer.MAX_VALUE : length<<1];
System.arraycopy(this.data, 0, data, 0, index);
//between here is exactely one free space at data[index]
System.arraycopy(this.data, index, data, index+1, this.size - index);
this.data = data;
}
else {
System.arraycopy(data, index, data, index+1, this.size - index);
}
data[index] = element;
this.size++;
}
/**
* @param c
* @return
*/
@Override
public boolean addAll(final Collection extends E> c)
{
int size = this.size;
Object[] data = this.data;
final int cSize = c.size();
if(cSize == 0) return false;
final int targetCapacity = this.calculateCapacityIncrease(cSize);
if(targetCapacity > data.length){
data = new Object[targetCapacity];
System.arraycopy(this.data, 0, data, 0, size);
this.data = data;
}
for(final E ec: c){
data[size++] = ec;
}
this.size = size;
return true;
}
/**
* @param index
* @param c
* @return
*/
@Override
public boolean addAll(final int index, final Collection extends E> c)
{
int size = this.size;
if(index == size){
return this.addAll(c);
}
else if(index > size || index < 0){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
final int cSize = c.size(); //just in case c's implementation has some stupid way to determine size
if(cSize == 0) return false;
if(c instanceof GrowList>){
final GrowList> gl = (GrowList>)c;
if(Integer.MAX_VALUE - cSize > this.size){
throw new ArrayIndexOutOfBoundsException(this.exceptionStringArrayLengthExceeded(cSize));
}
this.internalInsertArray(index, gl.data, cSize);
return true;
}
Object[] data = this.data;
final int targetCapacity = this.calculateCapacityIncrease(cSize);
if(targetCapacity > data.length){
data = new Object[targetCapacity];
System.arraycopy(this.data, 0, data, 0, index);
System.arraycopy(this.data, index, data, index+cSize, size-index);
this.data = data;
}
else {
// make room for all of c's elements
System.arraycopy(data, index, data, index+cSize, size-index);
}
// insert c's elements
for(final E e : c){
data[size++] = e;
}
this.size = size;
return true;
}
/**
* Clears the list by subsequently setting all storage slots to null.
* Note that {@link #truncate()} is considerably faster for large lists.
*
* @see #truncate()
*/
@Override
public void clear()
{
final Object[] data = this.data;
for(int i = this.size; i --> 0;){
data[i] = null;
}
this.size = 0;
}
/**
* @param o
* @return
*/
@Deprecated
@Override
public boolean contains(final Object o)
{
final Object[] data = this.data;
if(o == null){
for(int i = 0, size = this.size; i < size; i++){
if(data[i] == null) return true;
}
}
else {
for(int i = 0, size = this.size; i < size; i++){
if(o.equals(data[i])) return true;
}
}
return false;
}
/**
* @param c
* @return
*/
@Deprecated
@Override
public boolean containsAll(final Collection> c)
{
final Object[] data = this.data;
final int size = this.size;
boolean nullAreadyHandled = false;
collectionLoop: for(final Object o : c) {
if(o == null){
if(nullAreadyHandled) continue;
nullAreadyHandled = true;
for(int i = 0; i < size; i++){
if(data[i] == null) continue collectionLoop;
}
}
else {
for(int i = 0; i < size; i++){
if(o.equals(data[i])) continue collectionLoop;
}
}
return false;
}
//all elements of c have been found
return true;
}
/**
* @param index
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E get(final int index) throws ArrayIndexOutOfBoundsException
{
if(index >= this.size){
throw new ArrayIndexOutOfBoundsException(index);
}
return (E)this.data[index];
}
/**
* @param o
* @return
*/
@Deprecated
@Override
public int indexOf(final Object o)
{
if(o == null){
return this.indexOfId(null);
}
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
if(o.equals(data[i])){
return i;
}
}
return -1;
}
/**
* @return
*/
@Override
public boolean isEmpty()
{
return this.size == 0;
}
/**
* @return
*/
@Override
public Iterator iterator()
{
return new Itr();
}
/**
* @param o
* @return
*/
@Deprecated
@Override
public int lastIndexOf(final Object o)
{
final Object[] data = this.data;
if(o == null){
for(int i = this.size; i --> 0;){
if(data[i] == null){
return i;
}
}
}
else {
for(int i = this.size; i --> 0;){
if(o.equals(data[i])){
return i;
}
}
}
return -1;
}
/**
* @return
*/
@Override
public ListIterator listIterator()
{
return new ListItr();
}
/**
* @param index
* @return
*/
@Override
public ListIterator listIterator(final int index)
{
return new ListItr(index);
}
/**
* @param o
* @return
*/
@Deprecated
@Override
public boolean remove(final Object o)
{
return removeAllFromArray(o, this.data, 0, this.size, this.data, 0, this.size, EQUALS) > 0;
}
/**
* @param index
* @return
*/
@Override
public E remove(final int index) throws IndexOutOfBoundsException, ArrayIndexOutOfBoundsException
{
if(index >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
@SuppressWarnings("unchecked")
final E oldValue = (E)this.data[index];
final int numMoved = this.size-1 - index;
if (numMoved > 0){
System.arraycopy(this.data, index+1, this.data, index, numMoved);
}
this.data[--this.size] = null;
return oldValue;
}
/**
* @param c
* @return
*/
@Deprecated
@Override
public boolean removeAll(final Collection> c)
{
final int removed = removeAllFromArray(c, this.data, 0, this.size, this.data, 0, this.size, EQUALS);
this.size -= removed;
return removed != 0;
}
/**
* @param c
* @return
*/
@Deprecated
@Override
public boolean retainAll(final Collection> c)
{
final Object[] data = this.data;
final int size = this.size;
final int removedCount;
try{
for(int i = 0; i < size; i++){
if(!c.contains(data[i])){
data[i] = REMOVE_MARKER;
}
}
}
finally{
removedCount = JaArrays.removeAllFromArray(REMOVE_MARKER, this.data, 0, size, this.data, 0, size);
}
this.size -= removedCount;
return removedCount != 0;
}
/**
* @return
*/
@Override
public int removeDuplicates(final boolean keepMultipleNullValues, final Equalator equalator)
{
return this.rngRemoveDuplicates(0, this.size-1, keepMultipleNullValues, equalator);
}
/**
* @param startIndex
* @param endIndex
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngRemoveDuplicates(
final int startIndex,
final int endIndex,
final boolean ignoreNulls,
final Equalator equalator
)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final int removedCount;
final Object[] data = this.data;
try{
for(int i = startIndex-d; i != endIndex;){
final Object ei = data[i+=d];
if(ei == REMOVE_MARKER || ei == null && ignoreNulls) continue;
for(int j = i+d; j != endIndex;){
final Object ej = data[j+=d];
if(ej == REMOVE_MARKER) continue;
if(equalator.equal((E)ei, (E)ej)){
data[j] = REMOVE_MARKER;
}
}
}
}
finally{
removedCount = JaArrays.removeAllFromArray(
REMOVE_MARKER, this.data, startIndex, endIndex+1, this.data, startIndex, endIndex+1
);
}
this.size -= removedCount;
return removedCount;
}
/**
* @return
*/
@Override
public int removeDuplicates(final boolean ignoreNulls)
{
return this.rngRemoveDuplicates(0, this.size-1, ignoreNulls);
}
/**
* @param startIndex
* @param endIndex
* @return
*/
@Override
public int rngRemoveDuplicates(final int startIndex, final int endIndex, final boolean ignoreNulls)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
boolean alreadyFoundOneNull = false;
final int removedCount;
final Object[] data = this.data;
try{
for(int i = startIndex-d; i != endIndex;){
final Object ei = data[i+=d];
if(ei == REMOVE_MARKER) continue;
if(ei == null){
if(ignoreNulls) continue;
else if(alreadyFoundOneNull){
data[i] = REMOVE_MARKER;
}
else {
alreadyFoundOneNull = true;
}
continue;
}
for(int j = i; j != endIndex;){
final Object ej = data[j+=d];
if(ej == REMOVE_MARKER) continue;
if(ei == ej){
data[j] = REMOVE_MARKER;
}
}
}
}
finally{
removedCount = JaArrays.removeAllFromArray(
REMOVE_MARKER, this.data, startIndex, endIndex+1, this.data, startIndex, endIndex+1
);
}
this.size -= removedCount;
return removedCount;
}
/**
* @param fromIndex
* @param toIndex
* @return
*/
@Override
public GrowList removeRange(int startIndex, int endIndex)
{
if(startIndex > endIndex){
final int t = startIndex;
startIndex = endIndex;
endIndex = t;
}
if(startIndex < 0 || endIndex >= this.size){
this.exceptionStringRange(startIndex, endIndex);
}
final Object[] data = this.data;
final int size = this.size;
System.arraycopy(data, endIndex+1, data, startIndex, size - endIndex - 1);
this.size -= endIndex - startIndex + 1;
// free old array buckets
for(int i = this.size; i < size; i++){
data[i] = null;
}
return this;
}
/**
* @param index
* @param element
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E set(final int index, final E element) throws IndexOutOfBoundsException, ArrayIndexOutOfBoundsException
{
if(index >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
final E old = (E)this.data[index];
this.data[index] = element;
return old;
}
/**
* @return
*/
@Override
public int size()
{
return this.size;
}
/**
* @param fromIndex
* @param toIndex
* @return
*/
@Override
public SubList subList(final int fromIndex, final int toIndex)
{
// range check is done in Constructor already
return new SubList(this, fromIndex, toIndex);
}
/**
* @return
*/
@Override
public Object[] toArray()
{
final Object[] array = new Object[this.size];
System.arraycopy(this.data, 0, array, 0, this.size);
return array;
}
/**
* @param
* @param a
* @return
*/
@SuppressWarnings("unchecked")
@Override
public T[] toArray(final T[] a)
{
final int size = this.size;
if(a.length < size){
// Make a new array of a's runtime type, but this list's contents:
return (T[])Arrays.copyOf(this.data, size, a.getClass());
}
System.arraycopy(this.data, 0, a, 0, size);
if(a.length > size){
a[size] = null;
}
return a;
}
/**
* @param startIndex
* @param endIndex
* @param a
* @param
* @return
*/
@SuppressWarnings("unchecked")
@Override
public T[] rngToArray(final int startIndex, final int endIndex, T[] a)
{
final Object[] data = this.data;
final boolean incrementing;
final int targetLength;
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
incrementing = true;
targetLength = endIndex - startIndex + 1;
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
incrementing = false;
targetLength = startIndex - endIndex + 1;
}
if(a.length < targetLength){
a = (T[])Array.newInstance(a.getClass().getComponentType(), targetLength);
}
if(incrementing){
System.arraycopy(data, startIndex, a, 0, targetLength);
}
else {
for(int i = startIndex, j = 0; i >= endIndex; i--){
a[j++] = (T)data[i];
}
}
if(a.length > this.size){
a[this.size] = null;
}
return a;
}
/**
*
* @param elements
* @return
*/
@Override
public GrowList add(final E... elements)
{
Object[] data = this.data;
final int targetCapacity = this.calculateCapacityIncrease(elements.length);
if(targetCapacity > data.length){
data = new Object[targetCapacity];
System.arraycopy(this.data, 0, data, 0, this.size);
this.data = data;
}
System.arraycopy(elements, 0, data, this.size, elements.length);
this.size += elements.length;
return this;
}
/**
* @param index
* @param elements
* @return
*/
@Override
public GrowList insert(final int index, final E... elements) throws IndexOutOfBoundsException
{
if(index < 0){
throw new ArrayIndexOutOfBoundsException(index);
}
if(Integer.MAX_VALUE - elements.length > this.size){
throw new ArrayIndexOutOfBoundsException(this.exceptionStringArrayLengthExceeded(elements.length));
}
if(index == this.size){
return this.add(elements);
}
if(index > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
this.internalInsertArray(index, elements, elements.length);
return this;
}
/**
* @param elements
* @return
*/
@Override
public GrowList add(final Iterable extends E> elements)
{
if(elements instanceof Collection>){
this.addAll((Collection extends E>)elements);
return this;
}
Object[] data = this.data;
int dataLength = data.length;
int size = this.size;
try{
for(final E t : elements) {
//can't foresee elements' size, so do it the hard way
if(size == dataLength){
//expandArray()
dataLength = dataLength == MAXIMUM_POW2_SIZE ?Integer.MAX_VALUE : dataLength<<1;
data = new Object[dataLength];
System.arraycopy(this.data, 0, data, 0, size);
this.data = data;
}
data[size++] = t;
}
}
finally{
// in case elements will overflow the max array size, size still has to be updated correctly
this.size = size;
}
return this;
}
/**
* @param index
* @param elements
* @return this instance.
*/
@Override
public GrowList insert(final int index, final Iterable extends E> elements)
{
if(index < 0){
throw new ArrayIndexOutOfBoundsException(index);
}
if(index > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringIndexOutOfBounds(index));
}
if(index == this.size){
return this.add(elements);
}
if(elements instanceof Collection>){
this.addAll(index, (Collection extends E>)elements);
return this;
}
/* Can't determine elements' length beforehand, so put all elements in a buffer and then copy the buffer array.
* Will very likely be faster than shifting the rest of the array on every element.
*/
final GrowList buffer = new GrowList();
buffer.add(elements);
if(Integer.MAX_VALUE - buffer.size > this.size){
throw new ArrayIndexOutOfBoundsException(this.exceptionStringArrayLengthExceeded(buffer.size));
}
this.internalInsertArray(index, buffer.data, buffer.size);
return this;
}
/**
* @param element
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int count(final E element, final Equalator equalator)
{
int count = 0;
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
if(equalator.equal(element, (E)data[i])){
count++;
}
}
return count;
}
/**
* @param startIndex
* @param endIndex
* @param element
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngCount(final int startIndex, final int endIndex, final E element, final Equalator equalator)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
int count = 0;
final Object[] data = this.data;
for(int i = startIndex; i != endIndex;){
if(equalator.equal(element, (E)data[i+=d])){
count++;
}
}
return count;
}
/**
* @param startIndex
* @param endIndex
* @param element
* @return
*/
@Override
public GrowList fill(int startIndex, int endIndex, final E element)
{
if(startIndex > endIndex){
final int t = startIndex;
startIndex = endIndex;
endIndex = t;
}
if(startIndex < 0 || endIndex >= this.size){
this.exceptionStringRange(startIndex, endIndex);
}
final Object[] data = this.data;
for(int i = startIndex; i <= endIndex; i++){
data[i] = element;
}
return this;
}
/**
* @param startIndex
* @param endIndex
* @param c
* @return
*/
@SuppressWarnings("unchecked")
@Override
public boolean rngContainsAll(
final int startIndex,
final int endIndex,
final Collection c,
final boolean ignoreNulls,
final Equalator equalator
)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
boolean nullAreadyHandled = ignoreNulls;
final Object[] data = this.data;
collectionLoop: for(final E ec : c) {
if(ec == null){
if(nullAreadyHandled) continue;
nullAreadyHandled = true;
}
for(int i = startIndex-d; i != endIndex;){
if(equalator.equal(ec, (E)data[i+=d])) continue collectionLoop;
}
return false;
}
return true; //all elements of c have been found
}
/**
*
* @return
*/
@SuppressWarnings("unchecked")
public E getFirst()
{
return (E)this.data[0];
}
/**
*
* @return
*/
@SuppressWarnings("unchecked")
public E getLast()
{
return (E)this.data[this.size-1];
}
/**
*
* @param element
*/
public void setFirst(final E element)
{
this.data[0] = element;
}
/**
*
* @param element
*/
public void setLast(final E element)
{
this.data[this.size-1] = element;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int count(final Predicate predicate)
{
int count = 0;
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
if(predicate.apply((E)data[i])){
count++;
}
}
return count;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int count(final TPredicate predicate)
{
int count = 0;
final Object[] data = this.data;
final int size = this.size;
int i = 0;
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
if(predicate.apply((E)data[i++])){
count++;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return count; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return count;
}
/**
* Immediately releases the current internal data array and initializes a new one with the default data size.
* The new size of this liste is 0.
*
* If the data is only to be cleared without new storage allocation, see {@link #clear()}.
* Note though, that reinitializing the list may be considerably faster than clearing for large lists.
* For smaller lists, {@link #clear()} will most probably be faster than {@link #truncate()}.
*
* @return this instance
* @see #clear()
*/
@Override
public GrowList truncate()
{
this.data = new Object[DEFAULT_DATA_SIZE];
this.size = 0;
return this;
}
/**
* @param startIndex
* @param o
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngIndexOf(final int startIndex, final int endIndex, final E o, final Equalator equalator)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
if(equalator.equal(o, (E)data[i+=d])){
return i;
}
}
return -1;
}
/**
* @param startIndex
* @param endIndex
* @param element
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E rngRemoveOne(final int startIndex, final int endIndex, final E element, final Equalator equalator)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
if(equalator.equal(element, (E)data[i+=d])){
final E oldValue = (E)data[i];
if(i < --this.size){
System.arraycopy(data, i+1, data, i, this.size - i);
}
data[this.size] = null;
return oldValue;
}
}
return null;
}
/**
* @param startIndex
* @param endIndex
* @param c
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngRetainAll(
final int startIndex, final int endIndex, final XCollection c, final Equalator equalator
)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final int removedCount;
final Object[] data = this.data;
boolean skipNulls = false;
Object e;
try {
for(int i = startIndex-d; i != endIndex;){
if((e = data[i+=d]) == null){
if(skipNulls) continue;
skipNulls = true;
}
if(c.contains((E)e, equalator)) continue;
data[i] = REMOVE_MARKER;
}
}
finally {
removedCount = d == -1
?removeAllFromArray(REMOVE_MARKER, this.data, endIndex, startIndex+1, this.data, endIndex, startIndex+1)
:removeAllFromArray(REMOVE_MARKER, this.data, startIndex, endIndex+1, this.data, startIndex, endIndex+1)
;
this.size -= removedCount;
}
return removedCount;
}
/**
* @param startIndex
* @param endIndex
* @return
*/
@Override
public Object[] rngToArray(final int startIndex, final int endIndex)
{
final Object[] data = this.data;
final boolean incrementing;
final int targetLength;
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
incrementing = true;
targetLength = endIndex - startIndex + 1;
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
incrementing = false;
targetLength = startIndex - endIndex + 1;
}
final Object[] array = new Object[targetLength];
if(incrementing){
System.arraycopy(data, startIndex, array, 0, targetLength);
}
else {
for(int i = startIndex, j = 0; i >= endIndex; i--){
array[j++] = data[i];
}
}
return array;
}
/**
* @param index
* @param element
* @return
*/
@Override
public GrowList insert(final int index, final E element)
{
this.add(index, element);
return this;
}
/**
* @param startIndex
* @param endIndex
* @param operation
* @return
*/
@SuppressWarnings("unchecked")
@Override
public GrowList rngExecute(
final int startIndex, final int endIndex, final TOperation operation
)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
//this construction yields the same performance as the loop without inner try, but can still continue the loop
int i = startIndex-d; //start i one before startIndex as (i+=d) only works as prefix ~crement
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
operation.execute((E)data[i+=d]);
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return this; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return this;
}
/**
* @param operation
* @return
*/
@SuppressWarnings("unchecked")
@Override
public GrowList execute(final TOperation operation)
{
final Object[] data = this.data;
final int size = this.size;
int i = 0;
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
operation.execute((E)data[i++]);
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return this; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return this;
}
/**
* @param operation
*/
@SuppressWarnings("unchecked")
public GrowList execute(final Operation operation)
{
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
operation.execute((E)data[i]);
}
return this;
}
/**
* @param startIndex
* @param endIndex
* @param operation
* @return
*/
@SuppressWarnings("unchecked")
@Override
public GrowList rngExecute(
final int startIndex, final int endIndex, final Operation operation
)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
operation.execute((E)data[i+=d]);
}
return this;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int indexOf(final Predicate predicate)
{
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
if(predicate.apply((E)data[i])){
return i;
}
}
return -1;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int indexOf(final TPredicate predicate)
{
//this construction yields the same performance as a loop without inner try, but can still continue the loop
final Object[] data = this.data;
final int size = this.size;
int i = 0;
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
if(predicate.apply((E)data[i++])){
return i;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return -1; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return -1;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngCount(final int startIndex, final int endIndex, final Predicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
int count = 0;
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
if(predicate.apply((E)data[i+=d])){
count++;
}
}
return count;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngCount(final int startIndex, final int endIndex, final TPredicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
int count = 0;
final Object[] data = this.data;
int i = startIndex-d;
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
if(predicate.apply((E)data[i+=d])){
count++;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return count; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return count;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngIndexOf(final int startIndex, final int endIndex, final Predicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
if(predicate.apply((E)data[i+=d])){
return i;
}
}
return -1;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public int rngIndexOf(final int startIndex, final int endIndex, final TPredicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
int i = startIndex-d; //start i one before startIndex as (i+=d) only works as prefix ~crement
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
if(predicate.apply((E)data[i+=d])){
return i;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return -1; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return -1;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
*/
@SuppressWarnings("unchecked")
@Override
public int rngReduce(final int startIndex, final int endIndex, final Predicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final int removedCount;
final Object[] data = this.data;
try {
for(int i = startIndex-d; i != endIndex;) {
if(predicate.apply((E)data[i+=d])) {
data[i] = REMOVE_MARKER;
}
}
}
finally {
removedCount = d == -1
?removeAllFromArray(REMOVE_MARKER, this.data, endIndex, startIndex+1, this.data, endIndex, startIndex+1)
:removeAllFromArray(REMOVE_MARKER, this.data, startIndex, endIndex+1, this.data, startIndex, endIndex+1)
;
this.size -= removedCount;
}
return removedCount;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
*/
@SuppressWarnings("unchecked")
@Override
public int rngReduce(final int startIndex, final int endIndex, final TPredicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final int removedCount;
final Object[] data = this.data;
try {
//this construction yields the same performance as the loop without inner try, but can still continue the loop
int i = startIndex-d; //start i one before startIndex as (i+=d) only works as prefix ~crement
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
if(predicate.apply((E)data[i+=d])){
data[i] = REMOVE_MARKER;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowContinue c){ /*Nothing*/ }
catch(final ThrowReturn r) { break; } //must still execute finally code and return removedCount
}
}
finally {
//can't abort/return until remove markers are cleared, so do this in any case
removedCount = d == -1
?removeAllFromArray(REMOVE_MARKER, this.data, endIndex, startIndex+1, this.data, endIndex, startIndex+1)
:removeAllFromArray(REMOVE_MARKER, this.data, startIndex, endIndex+1, this.data, startIndex, endIndex+1)
;
this.size -= removedCount;
}
return removedCount;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E rngSearch(final int startIndex, final int endIndex, final Predicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
final E e = (E)data[i+=d];
if(e != null && predicate.apply(e)){
return e;
}
}
return null;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E rngSearch(final int startIndex, final int endIndex, final TPredicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
//this construction yields the same performance as the loop without inner try, but can still continue the loop
final Object[] data = this.data;
int i = startIndex-d; //start i one before startIndex as (i+=d) only works as prefix ~crement
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
final E e = (E)data[i+=d];
if(e != null && predicate.apply(e)){
return e;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return null; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return null;
}
/**
* @param predicate
*/
@SuppressWarnings("unchecked")
@Override
public int reduce(final Predicate predicate)
{
final int size = this.size;
if(size == 0){
return 0;
}
final int removedCount;
final Object[] data = this.data;
try {
for(int i = 0; i < size; i++){
if(predicate.apply((E)data[i])){
data[i] = REMOVE_MARKER;
}
}
}
finally {
//even if predicate throws an execption, the remove markers have to be cleared
removedCount = JaArrays.removeAllFromArray(REMOVE_MARKER, data, 0, this.size, data, 0, this.size);
}
this.size -= removedCount;
return removedCount;
}
/**
* @param predicate
*/
@SuppressWarnings("unchecked")
@Override
public int reduce(final TPredicate predicate)
{
final int size = this.size;
if(size == 0){
return 0; // spare unnecessary traversal of the whole list in array util method
}
final int removedCount;
//this construction yields the same performance as a loop without inner try, but can still continue the loop
final Object[] data = this.data;
int i = -1;
try {
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
if(predicate.apply((E)data[++i])){
data[i] = REMOVE_MARKER;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { break; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
}
finally {
//can't return until remove markers are cleared, so do this in any case
removedCount = JaArrays.removeAllFromArray(REMOVE_MARKER, data, 0, size, data, 0, size);
this.size -= removedCount;
}
return removedCount;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E search(final Predicate predicate)
{
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
final E element = (E)data[i];
if(element != null && predicate.apply(element)){
return element;
}
}
return null;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public E search(final TPredicate predicate)
{
//this construction yields the same performance as a loop without inner try, but can still continue the loop
final Object[] data = this.data;
final int size = this.size;
int i = 0;
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
final E e = (E)data[i++];
if(e != null && predicate.apply(e)){
return e;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return null; }
catch(final ThrowContinue c){ /*Nothing*/ }
}
return null;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public boolean contains(final Predicate predicate)
{
final Object[] data = this.data;
for(int i = 0, size = this.size; i < size; i++){
final E element = (E)data[i];
if(predicate.apply(element)){
return true;
}
}
return false;
}
/**
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public boolean contains(final TPredicate predicate)
{
//this construction yields the same performance as a loop without inner try, but can still continue the loop
final Object[] data = this.data;
final int size = this.size;
int i = 0;
while(i < size){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i < size){
if(predicate.apply((E)data[i++])){
return true;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return false; }
catch(final ThrowContinue c){ /* Nothing */ }
}
return false;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public boolean rngContains(final int startIndex, final int endIndex, final Predicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
final Object[] data = this.data;
for(int i = startIndex-d; i != endIndex;){
if(predicate.apply((E)data[i+=d])){
return true;
}
}
return false;
}
/**
* @param startIndex
* @param endIndex
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
@Override
public boolean rngContains(final int startIndex, final int endIndex, final TPredicate predicate)
{
final int d; //bi-directional index movement
if(startIndex <= endIndex){
if(startIndex < 0 || endIndex >= this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, endIndex));
}
d = 1; //incrementing direction
}
else {
if(startIndex >= this.size || endIndex < 0){
throw new IndexOutOfBoundsException(this.exceptionStringRange(endIndex, startIndex));
}
d = -1; //decrementing direction
}
//this construction yields the same performance as the loop without inner try, but can still continue the loop
final Object[] data = this.data;
int i = startIndex-d; //start i one before startIndex as (i+=d) only works as prefix ~crement
while(i != endIndex){ //while(true) astonishingly is significantly slower than duplicated inner condition
try {
while(i != endIndex){
if(predicate.apply((E)data[i+=d])){
return true;
}
}
break;
}
catch(final ThrowBreak b) { break; }
catch(final ThrowReturn r) { return false; }
catch(final ThrowContinue c){ /* Nothing */ }
}
return false;
}
/**
* @param startIndex
* @param elements
* @return
* @see com.xdev.jadoth.collections.XList#set(int, E[])
*/
@Override
public GrowList set(final int startIndex, final E... elements)
{
if(startIndex < 0 || startIndex + elements.length > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, startIndex + elements.length - 1));
}
System.arraycopy(elements, 0, this.data, startIndex, elements.length);
return this;
}
/**
* @param startIndex
* @param elements
* @param elementsStartIndex
* @param length
* @return
* @see com.xdev.jadoth.collections.XList#set(int, E[], int, int)
*/
@Override
public GrowList set(final int startIndex, final E[] elements, final int elementsStartIndex, final int length)
{
if(startIndex < 0 || startIndex + length > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, startIndex + elements.length - 1));
}
System.arraycopy(elements, elementsStartIndex, this.data, startIndex, length);
return this;
}
/**
* @param startIndex
* @param elements
* @param elementsStartIndex
* @param length
* @return
* @see com.xdev.jadoth.collections.XList#set(int, List, int, int)
*/
@Override
public GrowList set(final int startIndex, final List elements, final int elementsStartIndex, final int length)
{
if(startIndex < 0 || startIndex + length > this.size){
throw new IndexOutOfBoundsException(this.exceptionStringRange(startIndex, startIndex + elements.size()-1));
}
final Object[] data = this.data;
int i = startIndex;
if(elements instanceof RandomAccess){
for(int ei = elementsStartIndex, bound = elementsStartIndex + length; ei < bound; ei++){
data[i++] = elements.get(ei);
}
}
else {
int ei = 0;
int count = 0;
for(final E e : elements){
if(ei++ < elementsStartIndex) continue;
data[i++] = e;
if(++count == length) break;
}
}
return this;
}
/**
* Sorts this list using the Quicksort algorithm.
* As a rule of thumb, use this algorithm if sorting stability is not relevant
* and if the list doesn't happen to be presorted often.
* Otherwise, use {@link #sortMerge(Comparator)}.
*
* Performance
*
Best case : fastest known algorithm [O(n log n)]
*
Average : fastest known algorithm [O(n log n)]
*
Worst case: theoretically slow [O(n^2)], but hardly relevant in practice. More like O(k * n log n)
*
Presorted : Depends on pivot element.
* Choosing the middle element yields like O(k * n log n) in tests.
*
* Characteristics
*
Stable : no
*
In-place : yes (plus recursive call stack)
*
* @param comparator the comparator to use for sorting the elements.
* @return this list.
* @see JaSort#quicksort(Object[], Comparator)
*/
@SuppressWarnings("unchecked")
public GrowList sortQuick(final Comparator comparator)
{
JaSort.quicksort(this.data, 0, this.size-1, (Comparator