All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.magicwerk.brownies.collections.IList Maven / Gradle / Ivy

Go to download

Brownies Collections complements the Java Collections Framework. GapList combines the strengths of both ArrayList and LinkedList. BigList is a list optimized for storing large number of elements. There are specialized List implementations for all primitive data types (IntGapList, IntBigList, IntObjGapList, IntObjBigList). The key collection classes offer support for keys and constraints for lists and collections (KeyList, KeyCollection, KeySet, Key1List, Key1Collection, Key1Set, Key2List, Key2Collection, Key2Set).

There is a newer version: 0.9.16
Show newest version
/*
 * Copyright 2012 by Thomas Mauch
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: IList.java 3150 2016-04-13 14:29:16Z origo $
 */
package org.magicwerk.brownies.collections;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.Set;

import org.magicwerk.brownies.collections.function.IFunction;
import org.magicwerk.brownies.collections.function.IPredicate;

/**
 * IList is an abstract class which offers all interfaces offered by both ArrayList and LinkedList.
 * It also offers additional methods which are then available in all implementations of GapList and BigList.
 *
 * @author Thomas Mauch
 * @version $Id: IList.java 3150 2016-04-13 14:29:16Z origo $
 *
 * @param  type of elements stored in the list
 * @see	    java.util.List
 * @see	    java.util.Deque
 * @see	    java.util.ArrayList
 * @see	    java.util.LinkedList
 */
@SuppressWarnings("serial")
public abstract class IList
	// AbstractList provides method subList()
	extends AbstractList
	implements
		// All interfaces of ArrayList
		List, RandomAccess, Cloneable, Serializable,
		// Additional interfaces of LinkedList and ArrayDeque
		Deque {

	/**
	 * Copies the collection values into an array.
	 *
	 * @param coll   collection of values
	 * @return       array containing the collection values
	 */
	static Object[] toArray(Collection coll) {
    	Object[] values = coll.toArray();
    	// as in ArrayList: toArray() might (incorrectly) not return Object[] (see bug 6260652)
    	if (values.getClass() != Object[].class) {
    		values = Arrays.copyOf(values, values.length, Object[].class);
	    }
    	return values;
	}

    /**
     * Returns a shallow copy of this list instance.
     * (the new list will contain the same elements as the source list, i.e. the elements themselves are not copied).
     * This method is identical to clone() except that the result is casted to an IList.
     *
     * @return a clone of this instance
     * @see #clone
     */
	@SuppressWarnings("unchecked")
    public IList copy() {
	    return (IList) clone();
	}

    /**
     * Returns an unmodifiable view of this list. This method allows
     * modules to provide users with "read-only" access to internal lists.
     * Query operations on the returned list "read through" to the specified
     * list, and attempts to modify the returned list, whether direct or
     * via its iterator, result in an UnsupportedOperationException.
     *
     * @return an unmodifiable view of the specified list
     */
    abstract public IList unmodifiableList();

    /**
     * Returns a shallow copy of this list instance.
     * (The elements themselves are not copied).
     *
     * @return a clone of this list instance
     */
	@SuppressWarnings("unchecked")
    @Override
    public Object clone() {
		try {
			IList list = (IList) super.clone();
			list.doClone(this);
		    return list;
		}
		catch (CloneNotSupportedException e) {
		    // This shouldn't happen, since we are Cloneable
		    throw new AssertionError(e);
		}
    }

	/**
	 * Initialize this object after the bitwise copy has been made
	 * by Object.clone().
	 *
	 * @param that	source object
	 */
	abstract protected void doClone(IList that);

	@Override
	public void clear() {
		doClear();
	}

	protected void doClear() {
		doRemoveAll(0, size());
	}

	/**
     * Resizes the list so it will afterwards have a size of
     * len. If the list must grow, the specified
     * element elem will be used for filling.
     *
     * @param len  	length of list
     * @param elem 	element which will be used for extending the list
     * @throws 	 	IndexOutOfBoundsException if the range is invalid
	 */
	public void resize(int len, E elem) {
	    checkLength(len);

	    int size = size();
        if (len < size) {
            remove(len, size-len);
        } else {
            for (int i=size; iGapList instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */
	// Note: Provide this method to make transition from ArrayList as
	//       smooth as possible
    public void ensureCapacity(int minCapacity) {
		doModify();
		doEnsureCapacity(minCapacity);
    }

    /**
     * Increases the capacity of this list instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */
    abstract protected void doEnsureCapacity(int minCapacity);

    /**
     * An application can use this operation to minimize the storage of an instance.
     */
	// Note: Provide this method to make transition from ArrayList as
	//       smooth as possible
	abstract public void trimToSize();

    @Override
    public boolean equals(Object obj) {
    	if (obj == this) {
    		return true;
    	}
    	if (!(obj instanceof List)) {
    		return false;
    	}
    	@SuppressWarnings("unchecked")
		List list = (List) obj;
    	int size = size();
    	if (size != list.size()) {
    		return false;
    	}
    	for (int i=0; i 0) {
				buf.append(", ");
			}
			buf.append(doGet(i));
		}
		buf.append("]");
		return buf.toString();
	}

	@Override
	public boolean isEmpty() {
		return size() == 0;
	}

	/**
	 * Helper function to check two elements stored in the list for equality.
	 *
	 * @param elem1	first element
	 * @param elem2	second element
	 * @return		true if the elements are equal, otherwise false
	 */
	static boolean equalsElem(Object elem1, Object elem2) {
		if (elem1 == null) {
			if (elem2 == null) {
				return true;
			}
		} else {
			if (elem1.equals(elem2)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Helper method to calculate hash code of a element stored in the list.
	 *
	 * @param elem	element
	 * @return		hash code for element
	 */
	static int hashCodeElem(Object elem) {
	    if (elem == null) {
	        return 0;
	    } else {
	        return elem.hashCode();
	    }
	}

	/**
	 * Counts how many times the specified element is contained in the list.
	 *
	 * @param elem	element to count
	 * @return		count how many times the specified element is contained in the list
	 */
	public int getCount(E elem) {
		int count = 0;
		int size = size();
		for (int i=0; i getAll(E elem) {
        IList list = doCreate(-1);
		int size = size();
		for (int i=0; i getWhere(IPredicate predicate) {
        IList list = doCreate(-1);
		int size = size();
		for (int i=0; i predicate) {
	    int size = size();
		for (int i=0; i predicate) {
	    int size = size();
		for (int i=0; i extractWhere(IPredicate predicate) {
	    IList list = doCreate(-1);
	    int size = size();
		for (int i=0; i getDistinct() {
        Set set = new HashSet();
		int size = size();
		for (int i=0; i IList mappedList(IFunction mapper) {
		int size = size();
    	@SuppressWarnings("unchecked")
		IList mappedList = (IList) doCreate(size);
		for (int i=0; i predicate) {
    	// It is typically faster to copy the allowed elements in a new list
    	// than to remove the not allowed from the existing one
    	IList list = doCreate(-1);
		int size = size();
		for (int i=0; i=0; i--) {
			if (equalsElem(doGet(i), elem)) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Returns the index of the first occurrence of the specified element in this list, starting the search at the specified position.
	 * If the element is not found, -1 is returned.
	 *
	 * @param elem			element to search for
	 * @param fromIndex		start index for search
	 * @return				the index of the first occurrence of the specified element in this list that is greater than or equal to fromIndex,
	 * 						or -1 if this list does not contain the element
	 * @see #indexOf(Object)
	 */
	public int indexOf(Object elem, int fromIndex) {
		if (fromIndex < 0) {
			fromIndex = 0;
		}
		int size = size();
		for (int i=fromIndex; i= size) {
			fromIndex = size-1;
		}
		for (int i=fromIndex; i>=0; i--) {
			if (equalsElem(doGet(i), elem)) {
				return i;
			}
		}
		return -1;
	}

	@Override
	public boolean remove(Object elem) {
		int index = indexOf(elem);
		if (index == -1) {
			return false;
		}
		doRemove(index);
		return true;
	}

	@Override
	public boolean contains(Object elem) {
		return indexOf(elem) != -1;
	}

	/**
	 * Add elements if it is not already contained in the list.
	 *
	 * @param elem	element to add
	 * @return		true if element has been added, false if not
	 */
	// CopyOnWriteArrayList contains methods addIfAbsent() and addAllAbsent()
	public boolean addIfAbsent(E elem) {
		if (contains(elem)) {
			return false;
		}
		return add(elem);
	}

	/**
	 * Returns true if any of the elements of the specified collection is contained in the list.
	 *
	 * @param coll collection with elements to be contained
	 * @return     true if any element is contained, false otherwise
	 */
	public boolean containsAny(Collection coll) {
	    // Note that the signature has been chosen as in List:
	    // - boolean addAll(Collection c);
	    // - boolean containsAll(Collection c);
	    for (Object elem: coll) {
	        if (contains(elem)) {
	            return true;
	        }
	    }
	    return false;
	}

	@Override
	public boolean containsAll(Collection coll) {
	    // Note that this method is already implemented in AbstractCollection.
		// It has been duplicated so the method is also available in the primitive classes.
	    for (Object elem: coll) {
	        if (!contains(elem)) {
	            return false;
	        }
	    }
	    return true;
	}

	/**
	 * Removes all equal elements.
	 *
	 * @param elem	element
	 * @return		removed equal elements (never null)
	 */
    public IList removeAll(E elem) {
	    IList list = doCreate(-1);
	    int size = size();
		for (int i=0; i coll) {
	    // Note that this method is already implemented in AbstractCollection.
		// It has been duplicated so the method is also available in the primitive classes.
    	checkNonNull(coll);
	    boolean modified = false;
	    int size = size();
		for (int i=0; i coll) {
    	// There is a special implementation accepting an IList
    	// so the method is also available in the primitive classes.
    	checkNonNull(coll);
	    boolean modified = false;
	    int size = size();
		for (int i=0; i coll) {
	    // Note that this method is already implemented in AbstractCollection.
		// It has been duplicated so the method is also available in the primitive classes.
    	checkNonNull(coll);
	    boolean modified = false;
	    int size = size();
		for (int i=0; i coll) {
    	// There is a special implementation accepting an IList
    	// so the method is also available in the primitive classes.
    	checkNonNull(coll);
	    boolean modified = false;
	    int size = size();
		for (int i=0; i T[] toArray(T[] array) {
	    int size = size();
        if (array.length < size) {
        	array = (T[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), size);
        }
        doGetAll(array, 0, size);
        if (array.length > size) {
        	array[size] = null;
        }
        return array;
	}

	/**
	 * Helper method to fill the specified elements in an array.
	 *
	 * @param array	array to store the list elements
	 * @param index	index of first element to copy
	 * @param len	number of elements to copy
	 * @param  type of elements stored in the list
	 */
	@SuppressWarnings("unchecked")
	protected  void doGetAll(T[] array, int index, int len) {
		for (int i=0; i list) {
		int listSize = list.size();
		doEnsureCapacity(size() + listSize);

		if (listSize == 0) {
			return false;
		}

		boolean changed = false;
		int prevSize = size();
		for (int i=0; i iterator() {
		return new Iter(true);
	}

    @Override
	public ListIterator listIterator() {
		return new ListIter(0);
	}

    @Override
	public ListIterator listIterator(int index) {
		return new ListIter(index);
	}

	@Override
	public Iterator descendingIterator() {
		return new Iter(false);
	}

    // Queue operations

    @Override
    public E peek() {
        if (size() == 0) {
            return null;
        }
        return getFirst();
    }

    @Override
    public E element() {
    	// inline version of getFirst():
        if (size() == 0) {
            throw new NoSuchElementException();
        }
    	return doGet(0);
    }

    @Override
    public E poll() {
        if (size() == 0) {
            return null;
        }
    	return doRemove(0);
    }

	@Override
    public E remove() {
		// inline version of removeFirst():
        if (size() == 0) {
            throw new NoSuchElementException();
        }
    	return doRemove(0);
    }

	@Override
    public boolean offer(E elem) {
    	// inline version of add(elem):
    	return doAdd(-1, elem);
    }

    // Deque operations

    @Override
    public E getFirst() {
        if (size() == 0) {
            throw new NoSuchElementException();
        }
    	return doGet(0);
    }

    @Override
    public E getLast() {
    	int size = size();
        if (size == 0) {
            throw new NoSuchElementException();
        }
    	return doGet(size-1);
    }

    @Override
    public void addFirst(E elem) {
    	doAdd(0, elem);
    }

    @Override
    public void addLast(E elem) {
    	// inline version of add(elem):
    	doAdd(-1, elem);
    }

    @Override
    public E removeFirst() {
        if (size() == 0) {
            throw new NoSuchElementException();
        }
    	return doRemove(0);
    }

    @Override
    public E removeLast() {
    	int size = size();
        if (size == 0) {
            throw new NoSuchElementException();
        }
    	return doRemove(size-1);
    }

	@Override
	public boolean offerFirst(E elem) {
        // inline version of addFirst(elem):
    	doAdd(0, elem);
        return true;
	}

	@Override
	public boolean offerLast(E elem) {
        // inline version of addLast(elem):
    	doAdd(-1, elem);
        return true;
	}

	@Override
	public E peekFirst() {
        if (size() == 0) {
            return null;
        }
        return doGet(0);
	}

	@Override
	public E peekLast() {
		int size = size();
        if (size == 0) {
            return null;
        }
        return doGet(size-1);
	}

	@Override
	public E pollFirst() {
        if (size() == 0) {
            return null;
        }
        return doRemove(0);
	}

	@Override
	public E pollLast() {
		int size = size();
        if (size == 0) {
            return null;
        }
        return doRemove(size-1);
	}

	@Override
	public E pop() {
        // inline version of removeFirst():
        if (size() == 0) {
            throw new NoSuchElementException();
        }
    	return doRemove(0);

	}

	@Override
	public void push(E elem) {
        // inline version of addFirst();
    	doAdd(0, elem);
	}

	@Override
	public boolean removeFirstOccurrence(Object elem) {
		int index = indexOf(elem);
		if (index == -1) {
			return false;
		}
		doRemove(index);
		return true;
	}

	@Override
	public boolean removeLastOccurrence(Object elem) {
		int index = lastIndexOf(elem);
		if (index == -1) {
			return false;
		}
		doRemove(index);
		return true;
	}

    // --- Static bulk transfer methods working with two ILists ---

    /**
     * Copies elements from one list to another.
     * Elements and size of source list do not change.
     * The elements in the specified range in the destination list are removed and
     * the elements specified to be copied are inserted.
     *
     * If source and destination list are identical, the method behaves like {@link #copy(int, int, int)}.
     *
     * @param src		source list
     * @param srcIndex	index of first element in source list
     * @param srcLen	number of elements to copy
     * @param dst		destination list
     * @param dstIndex	index of first element in destination list
     * @param dstLen	number of elements to replace in destination list
     * @param  		type of elements stored in the list
     * @throws 			IndexOutOfBoundsException if the ranges are invalid
     */
    public static  void transferCopy(IList src, int srcIndex, int srcLen, IList dst, int dstIndex, int dstLen) {
    	if (src == dst) {
    		src.checkLengths(srcLen, dstLen);
    		src.copy(srcIndex, dstIndex, srcLen);
    	} else {
    		src.doTransfer(TRANSFER_COPY, srcIndex, srcLen, dst, dstIndex, dstLen);
    	}
    }

    /**
     * Moves elements from one list to another by setting it to null in the source list.
     * Elements in the source range are set to null, but size of source list does not change.
     * The elements in the specified range in the destination list are removed and
     * the elements specified to be moved are inserted.
     *
     * If source and destination list are identical, the method behaves like {@link #move(int, int, int)}.
     *
     * @param src		source list
     * @param srcIndex	index of first element in source list
     * @param srcLen	number of elements to copy
     * @param dst		destination list
     * @param dstIndex	index of first element in destination list
     * @param dstLen	number of elements to replace in destination list
     * @param  		type of elements stored in the list
     * @throws 			IndexOutOfBoundsException if the ranges are invalid
     */
    public static  void transferMove(IList src, int srcIndex, int srcLen, IList dst, int dstIndex, int dstLen) {
    	if (src == dst) {
    		src.checkLengths(srcLen, dstLen);
    		src.move(srcIndex, dstIndex, srcLen);
    	} else {
    		src.doTransfer(TRANSFER_MOVE, srcIndex, srcLen, dst, dstIndex, dstLen);
    	}
    }

    /**
     * Moves elements from one list to another by removing it from the source list.
     * So the size of source list will change.
     * The elements in the specified range in the destination list are removed and
     * the elements specified to be moved are inserted.
     *
     * If source and destination list are identical, the method behaves like {@link #drag(int, int, int)}.
     *
     * @param src		source list
     * @param srcIndex	index of first element in source list
     * @param srcLen	number of elements to copy
     * @param dst		destination list
     * @param dstIndex	index of first element in destination list
     * @param dstLen	number of elements to replace in destination list
     * @param  		type of elements stored in the list
     * @throws 			IndexOutOfBoundsException if the ranges are invalid
     */
    public static  void transferRemove(IList src, int srcIndex, int srcLen, IList dst, int dstIndex, int dstLen) {
    	if (src == dst) {
    		src.checkLengths(srcLen, dstLen);
    		src.drag(srcIndex, dstIndex, srcLen);
    	} else {
    		src.doTransfer(TRANSFER_REMOVE, srcIndex, srcLen, dst, dstIndex, dstLen);
    	}
    }

    private static final int TRANSFER_COPY = 0;
    private static final int TRANSFER_MOVE = 1;
    private static final int TRANSFER_REMOVE = 2;

    void doTransfer(int transferMode, int srcIndex, int srcLen, IList dst, int dstIndex, int dstLen) {
    	// Prepare arguments
    	if (srcLen == -1) {
    		srcLen = size()-srcIndex;
    	}
        checkRange(srcIndex, srcLen);
        if (dstIndex == -1) {
        	dstIndex = dst.size();
        } else {
            dst.checkIndexAdd(dstIndex);
        }
        if (dstLen == -1) {
        	dstLen = dst.size() - dstIndex;
        } else {
        	dst.checkLength(dstLen);
        }

        E defaultElem = getDefaultElem();
        if (dstLen > srcLen) {
        	// Remove elements from destination because the source range is smaller than the destination range
        	dst.remove(dstIndex, dstLen-srcLen);
        } else if (srcLen > dstLen) {
        	// Add elements to destination because the source range is larger than the destination range
        	dst.addMult(dstIndex, srcLen-dstLen, defaultElem);
        }

        // Overwrite the range starting at dstIndex with length srcIndex in dst
        if (transferMode == TRANSFER_MOVE) {
        	// Move
    		for (int i=0; i 		type of elements stored in the list
     * @throws 			IndexOutOfBoundsException if the ranges are invalid
     */
    public static  void transferSwap(IList src, int srcIndex, IList dst, int dstIndex, int len) {
    	if (src == dst) {
    		src.swap(srcIndex, dstIndex, len);
    	} else {
    		src.doTransferSwap(srcIndex, dst, dstIndex, len);
    	}
    }

    void doTransferSwap(int srcIndex, IList dst, int dstIndex, int len) {
    	checkRange(srcIndex, len);
    	dst.checkRange(dstIndex, len);

		for (int i=0; i doCreate(int capacity);

    /**
     * Assign this list the content of the that list.
     * This is done by bitwise copying so the that list should not be used afterwards.
     *
     * @param that list to copy content from
     */
	abstract protected void doAssign(IList that);


    /**
     * Returns specified range of elements from list.
     *
     * @param index index of first element to retrieve
     * @param len   number of elements to retrieve
     * @return      list containing the specified range of elements
     */
    public IList getAll(int index, int len) {
        checkRange(index, len);

        IList list = doCreate(len);
        for (int i=0; i extract(int index, int len) {
        checkRange(index, len);

        IList list = doCreate(len);
        for (int i=0; i=index; i--) {
			doRemove(i);
		}
	}

    // -- addAll()

    /**
     * Adds all of the elements in the specified list into this list.
     *
     * @param list collection containing elements to be added to this list
     * @return true if this list changed as a result of the call
     * @throws NullPointerException if the specified list is null
     */
    public boolean addAll(IList list) {
        return doAddAll(-1, list);
    }

    /**
     * Inserts all of the elements in the specified list into this
     * list, starting at the specified position.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (increases their indices).
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param list list containing elements to be inserted into this list
     * @return true if this list changed as a result of the call
     * @throws IndexOutOfBoundsException if the index is invalid
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, IList list) {
		checkIndexAdd(index);

		return doAddAll(index, list);
	}

    /**
     * Adds all of the elements in the specified collection into this list.
     * The new elements will appear in the list in the order that they
     * are returned by the specified collection's iterator.
     *
     * @param coll collection containing elements to be added to this list
     * @return true if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
	@Override
	public boolean addAll(Collection coll) {
    	if (coll instanceof List) {
    		return doAddAll(-1, new IReadOnlyListFromList((List) coll));
    	} else {
    		return doAddAll(-1, new IReadOnlyListFromCollection(coll));
    	}
	}

    /**
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (increases their indices).
     * The new elements will appear in the list in the order that they
     * are returned by the specified collection's iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param coll collection containing elements to be inserted into this list
     * @return true if this list changed as a result of the call
     * @throws IndexOutOfBoundsException if the index is invalid
     * @throws NullPointerException if the specified collection is null
     */
    @Override
    public boolean addAll(int index, Collection coll) {
        checkIndexAdd(index);

    	if (coll instanceof List) {
    		return doAddAll(index, new IReadOnlyListFromList((List) coll));
    	} else {
    		return doAddAll(index, new IReadOnlyListFromCollection(coll));
    	}
    }

    /**
     * Adds all specified elements into this list.
     *
     * @param elems elements to be added to this list
     * @return true if this list changed as a result of the call
     */
	public boolean addArray(E... elems) {
		return doAddAll(-1, new IReadOnlyListFromArray(elems));
	}

    /**
     * Inserts the specified elements into this list,
     * starting at the specified position.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (increases their indices).
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param elems elements to be inserted into this list
     * @return true if this list changed as a result of the call
     * @throws IndexOutOfBoundsException if the index is invalid
     */
    public boolean addArray(int index, E... elems) {
        checkIndexAdd(index);

		return doAddAll(index, new IReadOnlyListFromArray(elems));
    }

    /**
     * Adds element multiple time to list.
     *
     * @param elem element to be added to this list
     * @return true if this list changed as a result of the call
     */
	public boolean addMult(int len, E elem) {
		return doAddAll(-1, new IReadOnlyListFromMult(len, elem));
	}

    /**
     * Inserts element multiple time to list, starting at the specified position.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (increases their indices).
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param elem element to be inserted into this list
     * @return true if this list changed as a result of the call
     * @throws IndexOutOfBoundsException if the index is invalid
     */
    public boolean addMult(int index, int len, E elem) {
        checkIndexAdd(index);

		return doAddAll(index, new IReadOnlyListFromMult(len, elem));
    }

	// -- setAll()

    /**
     * Sets the specified elements.
     *
     * @param index index of first element to set
     * @param list  list with elements to set
     * @throws 		IndexOutOfBoundsException if the range is invalid
     */
    public void setAll(int index, IList list) {
    	int listSize = list.size();
        checkRange(index, listSize);

        doReplaceAll(index, listSize, list);
    }

    /**
     * Sets the specified elements.
     *
     * @param index index of first element to set
     * @param coll  collection with elements to set
     */
    public void setAll(int index, Collection coll) {
    	int collSize = coll.size();
        checkRange(index, collSize);

    	if (coll instanceof List) {
    		doReplaceAll(index, collSize, new IReadOnlyListFromList((List) coll));
    	} else {
    		doReplaceAll(index, collSize, new IReadOnlyListFromCollection(coll));
    	}
    }

    /**
     * Sets the specified elements.
     *
     * @param index 	index of first element to set
     * @param elemes	array with elements to set
     * @throws 			IndexOutOfBoundsException if the range is invalid
     */
    public void setArray(int index, E... elems) {
    	int arrayLen = elems.length;
        checkRange(index, arrayLen);

		doReplaceAll(index, arrayLen, new IReadOnlyListFromArray(elems));
    }

    /**
     * Sets the element multiple times.
     *
     * @param index index of first element to set
     * @param elem	element to set
     */
    public void setMult(int index, int len, E elem) {
        checkRange(index, len);

		doReplaceAll(index, len, new IReadOnlyListFromMult(len, elem));
    }

    // -- putAll()

    /**
     * Set or add the specified elements.
     *
     * @param index index of first element to set or add
     * @param list  list with elements to set or add
     */
    public void putAll(int index, IList list) {
        checkIndexAdd(index);
    	checkNonNull(list);

        int len = size()-index;
		if (list != null) {
			if (list.size() < len) {
				len = list.size();
			}
		}

    	// Call worker method
    	doReplaceAll(index, len, list);
    }

    /**
     * Set or add the specified elements.
     * If the index is smaller than the size of the list, the existing element is replaced.
     * If the index equals the size of the list, the element is added.
     *
     * @param index index of first element to set or add
     * @param coll  collection with elements to set or add
     */
    public void putAll(int index, Collection coll) {
    	if (coll instanceof IList) {
    		putAll(index, (IList) coll);
    	} else if (coll instanceof List) {
    		putAll(index, new IReadOnlyListFromList((List) coll));
    	} else {
    		putAll(index, new IReadOnlyListFromCollection(coll));
    	}
    }

    /**
     * Set or add the specified elements.
     * If the index is smaller than the size of the list, the existing element is replaced.
     * If the index equals the size of the list, the element is added.
     *
     * @param index index of first element to set or add
     * @param elems	array with elements to set or add
     */
    public void putArray(int index, E... elems) {
        putAll(index, new IReadOnlyListFromArray(elems));
    }

    /**
     * Set or add the specified element multiple times.
     * If the index is smaller than the size of the list, the existing element is replaced.
     * If the index equals the size of the list, the element is added.
     *
     * @param index index of first element to set or add
     * @param len 	element to set or add
     */
    public void putMult(int index, int len, E elem) {
		putAll(index, new IReadOnlyListFromMult(len, elem));
    }

    // -- initAll()

	/**
	 * Initializes the list so it will afterwards only contain the elements of the collection.
	 * The list will grow or shrink as needed.
	 *
	 * @param list 	list with elements
     * @throws 		IndexOutOfBoundsException if the length is invalid
	 */
    public void initAll(IList list) {
    	checkNonNull(list);
    	doReplaceAll(0, size(), list);
    }

	/**
	 * Initializes the list so it will afterwards only contain the elements of the collection.
	 * The list will grow or shrink as needed.
	 *
	 * @param coll 	collection with elements
     * @throws 		IndexOutOfBoundsException if the length is invalid
	 */
    public void initAll(Collection coll) {
    	if (coll instanceof IList) {
    		initAll((IList) coll);
    	} else if (coll instanceof List) {
    		initAll(new IReadOnlyListFromList((List) coll));
    	} else {
    		initAll(new IReadOnlyListFromCollection(coll));
    	}
    }

	/**
	 * Initializes the list so it will afterwards only contain the elements of the array.
	 * The list will grow or shrink as needed.
	 *
	 * @param elems array with elements
     * @throws 		IndexOutOfBoundsException if the length is invalid
	 */
	public void initArray(E... elems) {
    	initAll(new IReadOnlyListFromArray(elems));
	}

	/**
	 * Initializes the list so it will afterwards have a size of
	 * len and contain only the element elem.
	 * The list will grow or shrink as needed.
	 *
	 * @param len  	length of list
	 * @param elem 	element which the list will contain
     * @throws 		IndexOutOfBoundsException if the length is invalid
	 */
	public void initMult(int len, E elem) {
	    checkLength(len);

    	initAll(new IReadOnlyListFromMult(len, elem));
	}

	// -- replaceAll()

    /**
     * Replaces the specified range with new elements.
     * This method is very powerful as it offers the functionality of many other methods
     * which are therefore only offered for convenience: 
* - addAll(index, list) -> replaceAll(index, 0, list)
* - setAll(index, list) -> replaceAll(index, list.size(), list)
* - putAll(index, list) -> replaceAll(index, -1, list)
* - initAll(list) -> replaceAll(0, this.size(), list)
* - remove(index, list) -> replaceAll(index, list.size(), null)
* * @param index index of first element to replace, use -1 for the position after the last element (this.size()) * @param len number of elements to replace, use -1 for getting behavior of putAll() * @param coll collection with elements which replace the old elements, use null if elements should only be removed * @throws IndexOutOfBoundsException if the range is invalid */ public void replaceAll(int index, int len, Collection coll) { if (coll instanceof IList) { replaceAll(index, len, (IList) coll); } else if (coll instanceof List) { replaceAll(index, len, new IReadOnlyListFromList((List) coll)); } else { replaceAll(index, len, new IReadOnlyListFromCollection(coll)); } } /** * Replaces the specified range with new elements. * This method is very powerful as it offers the functionality of many other methods * which are therefore only offered for convenience:
* - addAll(index, list) -> replaceAll(index, 0, list)
* - setAll(index, list) -> replaceAll(index, list.size(), list)
* - putAll(index, list) -> replaceAll(index, -1, list)
* - initAll(list) -> replaceAll(0, this.size(), list)
* - remove(index, list) -> replaceAll(index, list.size(), null)
* * @param index index of first element to replace, use -1 for the position after the last element (this.size()) * @param len number of elements to replace, use -1 for getting behavior of putAll() * @param elems array with elements which replace the old elements, use null if elements should only be removed * @throws IndexOutOfBoundsException if the range is invalid */ public void replaceArray(int index, int len, E... elems) { replaceAll(index, len, new IReadOnlyListFromArray(elems)); } /** * Replaces the specified range with new elements. * This method is very powerful as it offers the functionality of many other methods * which are therefore only offered for convenience:
* - addAll(index, list) -> replaceAll(index, 0, list)
* - setAll(index, list) -> replaceAll(index, list.size(), list)
* - putAll(index, list) -> replaceAll(index, -1, list)
* - initAll(list) -> replaceAll(0, this.size(), list)
* - remove(index, list) -> replaceAll(index, list.size(), null)
* * @param index index of first element to replace, use -1 for the position after the last element (this.size()) * @param len number of elements to replace, use -1 for getting behavior of putAll() * @param numElems number of time element has to be added * @param elem element to add * @throws IndexOutOfBoundsException if the range is invalid */ public void replaceMult(int index, int len, int numElems, E elem) { replaceAll(index, len, new IReadOnlyListFromMult(numElems, elem)); } /** * Replaces the specified range with new elements. * This method is very powerful as it offers the functionality of many other methods * which are therefore only offered for convenience:
* - addAll(index, list) -> replaceAll(index, 0, list)
* - setAll(index, list) -> replaceAll(index, list.size(), list)
* - putAll(index, list) -> replaceAll(index, -1, list)
* - initAll(list) -> replaceAll(0, this.size(), list)
* - remove(index, list) -> replaceAll(index, list.size(), null)
* * @param index index of first element to replace, use -1 for the position after the last element (this.size()) * @param len number of elements to replace, use -1 for getting behavior of putAll() * @param list list with elements which replace the old elements, use null if elements should only be removed * @throws IndexOutOfBoundsException if the range is invalid */ public void replaceAll(int index, int len, IList list) { // Check arguments if (index == -1) { index = size(); } else { checkIndexAdd(index); } if (len == -1) { len = size()-index; if (list != null) { if (list.size() < len) { len = list.size(); } } } else { checkRange(index, len); } // Call worker method doReplaceAll(index, len, list); } // -- doReplaceAll() protected boolean doReplaceAll(int index, int len, IList list) { // There is a special implementation accepting an IList // so the method is also available in the primitive classes. assert(index >= 0 && index <= size()); assert(len >= 0 && index+len <= size()); int srcLen = 0; if (list != null) { srcLen = list.size(); } doEnsureCapacity(size()-len+srcLen); // Remove elements doRemoveAll(index, len); // Add elements for (int i=0; i 0 || srcLen > 0; } // /** * Fill list. * * @param elem element used for filling */ // see java.util.Arrays#fill public void fill(E elem) { int size = size(); for (int i=0; i=0; i--) { doReSet(dstIndex+i, doGet(srcIndex+i)); } } else if (srcIndex > dstIndex) { for (int i=0; i=0; i--) { doReSet(dstIndex+i, doGet(srcIndex+i)); } } else if (srcIndex > dstIndex) { for (int i=0; i dstIndex) { int fill = Math.min(len, srcIndex-dstIndex); setMult(srcIndex+len-fill, fill, null); } } /** * Drag specified elements. * Source and destination ranges may overlap. * The size of the list does not change and it contains the same elements as before, but in changed order. * * @param srcIndex index of first source element to move * @param dstIndex index of first destination element to move * @param len number of elements to move * @throws IndexOutOfBoundsException if the ranges are invalid */ public void drag(int srcIndex, int dstIndex, int len) { checkRange(srcIndex, len); checkRange(dstIndex, len); if (srcIndex < dstIndex) { doRotate(srcIndex, len+(dstIndex-srcIndex), dstIndex-srcIndex); } else if (srcIndex > dstIndex) { doRotate(dstIndex, len+(srcIndex-dstIndex), dstIndex-srcIndex); } } /** * Swap the specified elements in the list. * * @param index1 index of first element in first range to swap * @param index2 index of first element in second range to swap * @param len number of elements to swap * @throws IndexOutOfBoundsException if the ranges are invalid */ public void swap(int index1, int index2, int len) { checkRange(index1, len); checkRange(index2, len); if ((index1 < index2 && index1+len > index2) || index1 > index2 && index2+len > index1) { throw new IndexOutOfBoundsException("Swap ranges overlap"); } for (int i=0; i= 0 && distance < len); int num = 0; for (int start=0; num != len; start++) { E elem = doGet(index+start); int i = start; do { i += distance; if (i >= len) { i -= len; } elem = doReSet(index+i, elem); num++; } while (i != start); } } /** * Sort elements in the list using the specified comparator. * * @param comparator comparator to use for sorting * (null means the elements natural ordering should be used) * * @see Arrays#sort */ public void sort(Comparator comparator) { sort(0, size(), comparator); } /** * Sort specified elements in the list using the specified comparator. * * @param index index of first element to sort * @param len number of elements to sort * @param comparator comparator to use for sorting * (null means the elements natural ordering should be used) * @throws IndexOutOfBoundsException if the range is invalid * * @see Arrays#sort */ abstract public void sort(int index, int len, Comparator comparator); /* Question: Why is the signature of method binarySearch public int binarySearch(K key, Comparator comparator) and not public int binarySearch(E key, Comparator comparator) as you could expect? Answer: This allows to use the binarySearch method not only with keys of the type stored in the GapList, but also with any other type you are prepared to handle in you Comparator. So if we have a class Name and its comparator as defined in the following code snippets, both method calls are possible: new GapList().binarySearch(new Name("a"), new NameComparator()); new GapList().binarySearch("a", new NameComparator()); class Name { String name; public Name(String name) { this.name = name; } public String getName() { return name; } public String toString() { return name; } } static class NameComparator implements Comparator { @Override public int compare(Object o1, Object o2) { String s1; if (o1 instanceof String) { s1 = (String) o1; } else { s1 = ((Name) o1).getName(); } String s2; if (o2 instanceof String) { s2 = (String) o2; } else { s2 = ((Name) o2).getName(); } return s1.compareTo(s2); } } */ /** * Searches the specified range for an object using the binary * search algorithm. * * @param key the value to be searched for * @param comparator the comparator by which the list is ordered. * A null value indicates that the elements' * {@linkplain Comparable natural ordering} should be used. * @return index of the search key, if it is contained in the array; * otherwise, (-(insertion point) - 1). The * insertion point is defined as the point at which the * key would be inserted into the array: the index of the first * element greater than the key, or a.length if all * elements in the array are less than the specified key. Note * that this guarantees that the return value will be >= 0 if * and only if the key is found. * * @see Arrays#binarySearch */ public int binarySearch(K key, Comparator comparator) { return binarySearch(0, size(), key, comparator); } /** * Searches the specified range for an object using the binary * search algorithm. * * @param index index of first element to search * @param len number of elements to search * @param key the value to be searched for * @param comparator the comparator by which the list is ordered. * A null value indicates that the elements' * {@linkplain Comparable natural ordering} should be used. * @return index of the search key, if it is contained in the array; * otherwise, (-(insertion point) - 1). The * insertion point is defined as the point at which the * key would be inserted into the array: the index of the first * element greater than the key, or a.length if all * elements in the array are less than the specified key. Note * that this guarantees that the return value will be >= 0 if * and only if the key is found. * @throws IndexOutOfBoundsException if the range is invalid * * @see Arrays#binarySearch */ abstract public int binarySearch(int index, int len, K key, Comparator comparator); //--- Arguments check methods /** * Check that specified index is valid for getting/setting elements. * * @param index index to check * @throws IndexOutOfBoundsException if index is invalid */ protected void checkIndex(int index) { if (index < 0 || index >= size()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " (size: " + size() + ")"); } } /** * Check that specified index is valid for adding elements. * * @param index index to check * @throws IndexOutOfBoundsException if index is invalid */ protected void checkIndexAdd(int index) { if (index < 0 || index > size()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " (size: " + size() + ")"); } } /** * Check that specified range is valid. * * @param index start index of range to check * @param len number of elements in range to check * @throws IndexOutOfBoundsException if index is invalid */ protected void checkRange(int index, int len) { if (index < 0 || len < 0 || index+len > size()) { throw new IndexOutOfBoundsException("Invalid range: " + index + "/" + len + " (size: " + size() + ")"); } } /** * Check that specified length is valid (>= 0). * * @param len length to check * @throws IndexOutOfBoundsException if length is invalid */ protected void checkLength(int len) { if (len < 0) { throw new IndexOutOfBoundsException("Invalid length: " + len); } } /** * Check that both specified lengths are valid (>= 0) and equal. * * @param len1 length to check * @param len2 length to check * @throws IndexOutOfBoundsException if lengths are invalid */ protected void checkLengths(int len1, int len2) { if (len1 != len2) { throw new IndexOutOfBoundsException("Invalid lengths: " + len1 + ", " + len2); } if (len1 < 0) { throw new IndexOutOfBoundsException("Invalid length: " + len1); } if (len2 < 0) { throw new IndexOutOfBoundsException("Invalid length: " + len2); } } /** * Check that object is not null. * * @param obj object to check * @throws NullPointerException if object is null */ protected void checkNonNull(Object obj) { if (obj == null) { throw new NullPointerException("Argument may not be null"); } } // --- Start class Iter --- /** * Iterator supports forward and reverse iteration. */ class Iter implements Iterator { /** true if iterator moves forward */ boolean forward; /** current index */ int index; /** index where element will be removed if remove() is called */ int remove; /** * Constructor. * * @param forward true for forward access */ public Iter(boolean forward) { this.forward = forward; if (forward) { index = 0; } else { index = size()-1; } remove = -1; } @Override public boolean hasNext() { if (forward) { return index != size(); } else { return index != -1; } } @Override public E next() { if (forward) { if (index >= size()) { throw new NoSuchElementException(); } } else { if (index < 0) { throw new NoSuchElementException(); } } E elem = get(index); remove = index; if (forward) { index++; } else { index--; } return elem; } @Override public void remove() { if (remove == -1) { throw new IllegalStateException("No current element to remove"); } IList.this.remove(remove); if (index > remove) { index--; } remove = -1; } } // --- End class Iter --- // --- Start class ListIter --- /** * Iterator supports forward and reverse iteration. */ class ListIter implements ListIterator { /** current index */ int index; /** index where element will be removed if remove() is called */ int remove; /** * Constructor. * * @param index start index */ public ListIter(int index) { checkIndexAdd(index); this.index = index; this.remove = -1; } @Override public boolean hasNext() { return index < size(); } @Override public boolean hasPrevious() { return index > 0; } @Override public E next() { if (index >= size()) { throw new NoSuchElementException(); } E elem = IList.this.get(index); remove = index; index++; return elem; } @Override public int nextIndex() { return index; } @Override public E previous() { if (index <= 0) { throw new NoSuchElementException(); } index--; E elem = IList.this.get(index); remove = index; return elem; } @Override public int previousIndex() { return index-1; } @Override public void remove() { if (remove == -1) { throw new IllegalStateException("No current element to remove"); } IList.this.remove(remove); if (index > remove) { index--; } remove = -1; } @Override public void set(E e) { if (remove == -1) { throw new IllegalStateException("No current element to set"); } IList.this.set(remove, e); } @Override public void add(E e) { IList.this.add(index, e); index++; remove = -1; } } // --- End class ListIter --- protected static abstract class IReadOnlyList extends IList { @Override public IList unmodifiableList() { error(); return null; } @Override protected void doClone(IList that) { error(); } @Override public int capacity() { error(); return 0; } @Override protected E doSet(int index, E elem) { error(); return null; } @Override protected E doReSet(int index, E elem) { error(); return null; } @Override protected E getDefaultElem() { error(); return null; } @Override protected boolean doAdd(int index, E elem) { error(); return false; } @Override protected E doRemove(int index) { error(); return null; } @Override protected void doEnsureCapacity(int minCapacity) { error(); } @Override public void trimToSize() { error(); } @Override protected IList doCreate(int capacity) { error(); return null; } @Override protected void doAssign(IList that) { error(); } @Override public void sort(int index, int len, Comparator comparator) { error(); } @Override public int binarySearch(int index, int len, K key, Comparator comparator) { error(); return 0; } /** * Throw exception if an attempt is made to change an immutable list. */ private void error() { throw new UnsupportedOperationException("list is read-only"); } } protected static class IReadOnlyListFromArray extends IReadOnlyList { E[] array; IReadOnlyListFromArray(E[] array) { this.array = array; } @Override public int size() { return array.length; } @Override protected E doGet(int index) { return array[index]; } } protected static class IReadOnlyListFromMult extends IReadOnlyList { int len; E elem; IReadOnlyListFromMult(int len, E elem) { checkLength(len); this.len = len; this.elem = elem; } @Override public int size() { return len; } @Override protected E doGet(int index) { return elem; } } protected static class IReadOnlyListFromCollection extends IReadOnlyList { Object[] array; IReadOnlyListFromCollection(Collection coll) { array = coll.toArray(); } @Override public int size() { return array.length; } @SuppressWarnings("unchecked") @Override protected E doGet(int index) { return (E) array[index]; } } protected static class IReadOnlyListFromList extends IReadOnlyList { List list2; @SuppressWarnings("unchecked") IReadOnlyListFromList(List list) { this.list2 = (List) list; } @Override public int size() { return list2.size(); } @Override protected E doGet(int index) { return list2.get(index); } } }