/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.collections.bags; import java.util.Iterator; import java.util.List; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Function; import xxl.core.util.ArrayResizer; /** * This class provides an implementation of the Bag interface that * internally uses a resizable array to store its elements. <p> * * Each DynamicArrayBag instance has a capacity. The capacity is the size * of the array used to store the elements in the bag. It is always at * least as large as the bag size. As elements are inserted into a * DynamicArrayBag, its capacity may grow automatically. The details of * the growth policy are specified by the ArrayResizer.<p> * * The cursors returned by this class' <tt>cursor</tt>, * <tt>fifoCursor</tt>, <tt>lifoCursor</tt> and <tt>query</tt> methods are * <i>fail-fast</i>: if the bag is structurally modified at any time after * the cursor is created, in any way except through the cursor's own * methods, the cursor will throw a ConcurrentModificationException. Thus, * in the face of concurrent modification, the cursor fails quickly and * cleanly, rather than risking arbitrary, non-deterministic behavior at * an undetermined time in the future.<p> * * Usage example (1). * <pre> * // create a new dynamic array bag * * DynamicArrayBag bag = new DynamicArrayBag(); * * // create an iteration over 20 random Integers (between 0 and 100) * * Iterator iterator = new DiscreteRandomNumber(new JavaDiscreteRandomWrapper(100), 20); * * // insert all elements of the given iterator * * bag.insertAll(iterator); * * // create a cursor that iterates over the elements of the bag * * Cursor cursor = bag.cursor(); * * // print all elements of the cursor (bag) * * while (cursor.hasNext()) * System.out.println(cursor.next()); * * // close the open iterator, cursor and bag after use * * ((Cursor)iterator).close(); * cursor.close(); * bag.close(); * </pre> * * Usage example (2). * <pre> * // create an iteration over the Integer between 0 and 19 * * iterator = new Enumerator(20); * * // create a new dynamic array bag that contains all elements of the given iterator * * bag = new DynamicArrayBag(iterator); * * // create a cursor that iterates over the elements of the bag * * cursor = bag.cursor(); * * // remove every even Integer from the cursor (and the underlying dynamic array bag) * * while (cursor.hasNext()) { * int i = ((Integer)cursor.next()).intValue(); * if (i%2 == 0) * cursor.remove(); * } * * // create a cursor that iterates over the elements of the bag * * cursor = bag.cursor(); * * // print all elements of the cursor (bag) * * while (cursor.hasNext()) * System.out.println(cursor.next()); * * // close the open iterator, cursor and bag after use * * ((Cursor)iterator).close(); * cursor.close(); * bag.close(); * </pre> * * @see AbstractCursor * @see ArrayResizer * @see Cursor * @see Function * @see Iterator */ public class DynamicArrayBag extends ArrayBag { /** * A factory method to create a new DynamicArrayBag (see contract for * {@link Bag#FACTORY_METHOD FACTORY_METHOD} in interface Bag). It may * be invoked with a <i>parameter list</i> (for further * details see Function) of object arrays, an iterator or without any * parameters. A <i>parameter list</i> of object arrays will * be used to initialize the internally used array with the object * array at index 0 and an iterator will be used to insert the * contained elements into the new DynamicArrayBag. * * @see Function */ public static final Function<Object,DynamicArrayBag> FACTORY_METHOD = new AbstractFunction<Object,DynamicArrayBag> () { public DynamicArrayBag invoke () { return new DynamicArrayBag(); } public DynamicArrayBag invoke (Object iterator) { return new DynamicArrayBag((Iterator) iterator); } public DynamicArrayBag invoke (List<? extends Object> list) { return new DynamicArrayBag((Object[])list.get(0)); } }; /** * An ArrayResizer managing the growth policy for internally used * array. The ArrayResizer decides before an insertion or after an * removal of an element whether the array is to be resized or not. * * @see ArrayResizer */ protected ArrayResizer resizer; /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified array resizer. * The field <tt>array</tt> is set to the specified array, the field * <tt>last</tt> is set to the specified size - 1 and the field * <tt>resizer</tt> (growth policy) is set to the specified resizer. * * @param size the number of elements of the specified array which * should be used to initialize the internally used array. * @param array the object array that is used to initialize the * internally used array. * @param resizer an ArrayResizer managing the growth policy for the * internally used array. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. */ public DynamicArrayBag (int size, Object [] array, ArrayResizer resizer) throws IllegalArgumentException { super(size, array); this.resizer = resizer; } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified array resizer. * This constructor is equivalent to the call of * <code>DynamicArrayBag(array.length, array, resizer)</code>. * * @param array the object array that is used to initialize the * internally used array. * @param resizer an ArrayResizer managing the growth policy for the * internally used array. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. */ public DynamicArrayBag (Object [] array, ArrayResizer resizer) throws IllegalArgumentException { this(array.length, array, resizer); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameters. This constructor is equivalent to the call of * <code>DynamicArrayBag(size, array, new ArrayResizer(fmin, fover, funder))</code>. * * @param size the number of elements of the specified array which * should be used to initialize the internally used array. * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @param fover a control parameter for the growth policy. * @param funder a control parameter for the growth policy. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. * @see ArrayResizer#ArrayResizer(double, double, double) */ public DynamicArrayBag (int size, Object [] array, double fmin, double fover, double funder) throws IllegalArgumentException { this(size, array, new ArrayResizer(fmin, fover, funder)); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameters. This constructor is equivalent to the call of * <code>DynamicArrayBag(array.length, array, new ArrayResizer(fmin, fover, funder))</code>. * * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @param fover a control parameter for the growth policy. * @param funder a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double, double, double) */ public DynamicArrayBag (Object [] array, double fmin, double fover, double funder) { this(array.length, array, fmin, fover, funder); } /** * Constructs an empty bag with a growth polity that depends on the * specified <tt>double</tt> parameters. This constructor is equivalent * to the call of * <code>DynamicArrayBag(0, new Object[0], new ArrayResizer(fmin, fover, funder))</code>. * * @param fmin a control parameter for the growth policy. * @param fover a control parameter for the growth policy. * @param funder a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double, double, double) */ public DynamicArrayBag (double fmin, double fover, double funder) { this(new Object[0], fmin, fover, funder); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameters. This constructor is equivalent to the call of * <code>DyanmicArrayBag(size, array, new ArrayResizer(fmin, f))</code>. * * @param size the number of elements of the specified array which * should be used to initialize the internally used array. * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @param f a control parameter for the growth policy. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. * @see ArrayResizer#ArrayResizer(double, double) */ public DynamicArrayBag (int size, Object [] array, double fmin, double f) throws IllegalArgumentException { this(size, array, new ArrayResizer(fmin, f)); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameters. This constructor is equivalent to the call of * <code>DynamicArrayBag(array.length, array, new ArrayResizer(fmin, f))</code>. * * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @param f a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double, double) */ public DynamicArrayBag (Object [] array, double fmin, double f) { this(array.length, array, fmin, f); } /** * Constructs an empty bag with a growth polity that depends on the * specified <tt>double</tt> parameters. This constructor is * equivalent to the call of * <code>DynamicArrayBag(0, new Object[0], new ArrayResizer(fmin, f))</code>. * * @param fmin a control parameter for the growth policy. * @param f a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double, double) */ public DynamicArrayBag (double fmin, double f) { this(new Object[0], fmin, f); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameter. This constructor is equivalent to the call of * <code>DynamicArrayResizer(size, array, new ArrayResizer(fmin))</code>. * * @param size the number of elements of the specified array which * should be used to initialize the internally used array. * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. * @see ArrayResizer#ArrayResizer(double) */ public DynamicArrayBag (int size, Object [] array, double fmin) throws IllegalArgumentException { this(size, array, new ArrayResizer(fmin)); } /** * Constructs a bag containing the elements of the specified array * with a growth polity that depends on the specified <tt>double</tt> * parameter. This constructor is equivalent to the call of * <code>DynamicArrayBag(array.length, array, new ArrayResizer(fmin))</code>. * * @param array the object array that is used to initialize the * internally used array. * @param fmin a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double) */ public DynamicArrayBag (Object [] array, double fmin) { this(array.length, array, fmin); } /** * Constructs an empty bag with a growth polity that depends on the * specified <tt>double</tt> parameter. This constructor is equivalent * to the call of * <code>DynamicArrayBag(0, new Object[0], new ArrayResizer(fmin))</code>. * * @param fmin a control parameter for the growth policy. * @see ArrayResizer#ArrayResizer(double) */ public DynamicArrayBag (double fmin) { this(new Object[0], fmin); } /** * Constructs a bag containing the elements of the specified array * with a default growth polity. This constructor is equivalent to the * call of * <code>DynamicArrayBag(size, array, new ArrayResizer())</code>. * * @param size the number of elements of the specified array which * should be used to initialize the internally used array. * @param array the object array that is used to initialize the * internally used array. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. * @see ArrayResizer#ArrayResizer() */ public DynamicArrayBag (int size, Object [] array) throws IllegalArgumentException { this(size, array, new ArrayResizer()); } /** * Constructs a bag containing the elements of the specified array * with a default growth polity. This constructor is equivalent to the * call of * <code>DynamicArrayBag(array.length, array, new ArrayResizer())</code>. * * @param array the object array that is used to initialize the * internally used array. * @see ArrayResizer#ArrayResizer() */ public DynamicArrayBag (Object [] array) { this(array.length, array); } /** * Constructs an empty bag with a default growth polity. This * constructor is equivalent to the call of * <code>DynamicArrayBag(0, new Object[0], new ArrayResizer())</code>. * @see ArrayResizer#ArrayResizer() */ public DynamicArrayBag () { this(new Object[0]); } /** * Constructs a bag with a default growth polity containing the * elements of the specified iterator. This constructor calls the void * constructor and uses the insertAll method to insert the elements of * the specified iterator. * * @param iterator the iterator whose elements are to be placed into * this queue. */ public DynamicArrayBag (Iterator iterator) { this(); super.insertAll(iterator); } /** * Returns a cursor to iterate over the elements in this bag without * any predefined sequence. The cursor is specifying a <i>view</i> on * the elements of this bag so that closing the cursor takes no * effect on the bag (e.g., not closing the bag). The behavior * of the cursor is unspecified if this bag is modified while the * cursor is in progress in any way other than by calling the methods * of the cursor. So, when the implementation of this cursor cannot * guarantee that the cursor is in a valid state after modifing the * underlying bag every method of the cursor except <tt>close()</tt> * should throw a <tt>ConcurrentModificationException</tt>. * * @return a cursor to iterate over the elements in this bag without * any predefined sequence. */ public Cursor cursor () { return new AbstractCursor() { int pos = 0; public boolean hasNextObject() { return pos <= last; } public Object nextObject() { return array[pos++]; } public void remove () throws IllegalStateException { super.remove(); System.arraycopy(array, pos, array, pos-1, last+1-(pos--)); array = resizer.resize(array, last--); } public boolean supportsRemove() { return true; } public void update (Object object) throws IllegalStateException { super.update(object); array[pos-1] = object; } public boolean supportsUpdate() { return true; } }; } /** * Returns a cursor representing a LIFO (<i>last in, first out</i>) * iteration over the elements in this bag. The cursor is specifying * a <i>view</i> on the elements of this bag so that closing the * cursor takes no effect on the bag (e.g., not closing the bag). The * behavior of the cursor is unspecified if this bag is modified while * the cursor is in progress in any way other than by calling the * methods of the cursor. So, when the implementation of this cursor * cannot guarantee that the cursor is in a valid state after modifing * the underlying bag every method of the cursor except * <tt>close()</tt> should throw a * <tt>ConcurrentModificationException</tt>. * * @return a cursor representing a LIFO (<i>last in, first out</i>) * iteration over the elements in this bag. */ public Cursor lifoCursor () { return new AbstractCursor() { int pos = last; public boolean hasNextObject() { return pos >= 0; } public Object nextObject() { return array[pos--]; } public void remove () throws IllegalStateException { super.remove(); System.arraycopy(array, pos+2, array, pos+1, last-pos-1); array = resizer.resize(array, last--); } public boolean supportsRemove() { return true; } public void update (Object object) throws IllegalStateException { super.update(object); array[pos+1] = object; } public boolean supportsUpdate() { return true; } }; } /** * Adds the specified element to this bag. This method does not * perform any kind of <i>duplicate detection</i>. * * @param object element to be added to this bag. */ public void insert (Object object) { array = resizer.resize(array, (++last)+1); array[last] = object; } /** * Removes all of the elements from this bag. The bag will be empty * after this call so that <tt>size() == 0</tt>.<br> * This implementation creates a cursor by calling the cursor method * and iterates over it, removing each element using the * {@link Cursor#remove() Cursor.remove} operation. Most * implementations will probably choose to override this method for * efficiency. */ public void clear () { array = new Object[0]; last = -1; } }