/* * Primitive Collections for Java. * Copyright (C) 2002, 2003 S�ren Bak * * 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 2.1 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package worm.util.list; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import worm.util.IntCollection; import worm.util.hash.DefaultIntHashFunction; import worm.util.util.Exceptions; /** * This class represents an array implemenation of lists of * int values. * * @see java.util.ArrayList * * @author Søren Bak * @version 1.3 21-08-2003 19:27 * @since 1.0 */ public class IntArrayList extends AbstractIntList implements Cloneable, Serializable { private static final long serialVersionUID = 1L; /** Constant indicating relative growth policy. */ private static final int GROWTH_POLICY_RELATIVE = 0; /** Constant indicating absolute growth policy. */ private static final int GROWTH_POLICY_ABSOLUTE = 1; /** * The default growth policy of this list. * @see #GROWTH_POLICY_RELATIVE * @see #GROWTH_POLICY_ABSOLUTE */ private static final int DEFAULT_GROWTH_POLICY = GROWTH_POLICY_RELATIVE; /** The default factor with which to increase the capacity of this list. */ public static final double DEFAULT_GROWTH_FACTOR = 1.0; /** The default chunk size with which to increase the capacity of this list. */ public static final int DEFAULT_GROWTH_CHUNK = 10; /** The default capacity of this list. */ public static final int DEFAULT_CAPACITY = 10; /** The elements of this list (indices <tt>0</tt> to <tt>size-1</tt>). */ private transient int[] data; /** * The current size of this list. * @serial */ private int size; /** * The growth policy of this list (0 is relative growth, 1 is absolute growth). * @serial */ private int growthPolicy; /** * The growth factor of this list, if the growth policy is * relative. * @serial */ private double growthFactor; /** * The growth chunk size of this list, if the growth policy is * absolute. * @serial */ private int growthChunk; private IntArrayList(int capacity, int growthPolicy, double growthFactor, int growthChunk) { if (capacity < 0) { Exceptions.negativeArgument("capacity", String.valueOf(capacity)); } if (growthFactor < 0.0) { Exceptions.negativeArgument("growthFactor", String.valueOf(growthFactor)); } if (growthChunk < 0) { Exceptions.negativeArgument("growthChunk", String.valueOf(growthChunk)); } data = new int[capacity]; size = 0; this.growthPolicy = growthPolicy; this.growthFactor = growthFactor; this.growthChunk = growthChunk; } /** * Creates a new array list with capacity 10 and a relative * growth factor of 1.0. * * @see #IntArrayList(int,double) */ public IntArrayList() { this(DEFAULT_CAPACITY); } /** * Creates a new array list with the same elements as a * specified collection. The elements of the specified collection * are added to the end of the list in the collection's iteration * order. * * @param c * the collection whose elements to add to the new * list. * * @throws NullPointerException * if <tt>c</tt> is <tt>null</tt>. */ public IntArrayList(IntCollection c) { this(c.size()); addAll(c); } /** * Creates a new array list with the same elements as a * specified array. The elements of the specified array * are added to the end of the list in order of the array. * * @param a * the array whose elements to add to the new * list. * * @throws NullPointerException * if <tt>a</tt> is <tt>null</tt>. * * @since 1.1 */ public IntArrayList(int[] a) { this(a.length); System.arraycopy(a, 0, data, 0, a.length); size = a.length; } /** * Creates a new array list with a specified capacity and a * relative growth factor of 1.0. * * @param capacity * the initial capacity of the list. * * @see #IntArrayList(int,double) * * @throws IllegalArgumentException * if <tt>capacity</tt> is negative. */ public IntArrayList(int capacity) { this(capacity, DEFAULT_GROWTH_FACTOR); } /** * Creates a new array list with a specified capacity and * relative growth factor. * * <p>The array capacity increases to <tt>capacity()*(1+growthFactor)</tt>. * This strategy is good for avoiding many capacity increases, but * the amount of wasted memory is approximately the size of the list. * * @param capacity * the initial capacity of the list. * * @param growthFactor * the relative amount with which to increase the * the capacity when a capacity increase is needed. * * @throws IllegalArgumentException * if <tt>capacity</tt> is negative; * if <tt>growthFactor</tt> is negative. */ public IntArrayList(int capacity, double growthFactor) { this(capacity, GROWTH_POLICY_RELATIVE, growthFactor, DEFAULT_GROWTH_CHUNK); } /** * Creates a new array list with a specified capacity and * absolute growth factor. * * <p>The array capacity increases to <tt>capacity()+growthChunk</tt>. * This strategy is good for avoiding wasting memory. However, an * overhead is potentially introduced by frequent capacity increases. * * @param capacity * the initial capacity of the list. * * @param growthChunk * the absolute amount with which to increase the * the capacity when a capacity increase is needed. * * @throws IllegalArgumentException * if <tt>capacity</tt> is negative; * if <tt>growthChunk</tt> is negative. */ public IntArrayList(int capacity, int growthChunk) { this(capacity, GROWTH_POLICY_ABSOLUTE, DEFAULT_GROWTH_FACTOR, growthChunk); } // --------------------------------------------------------------- // Array management // --------------------------------------------------------------- /** * Computes the new capacity of the list based on a needed * capacity and the growth policy. * * @param capacity * the needed capacity of the list. * * @return the new capacity of the list. */ private int computeCapacity(int capacity) { int newcapacity; if (growthPolicy == GROWTH_POLICY_RELATIVE) { newcapacity = (int)(data.length * (1.0 + growthFactor)); } else { newcapacity = data.length + growthChunk; } if (newcapacity < capacity) { newcapacity = capacity; } return newcapacity; } /** * Ensures that this list has at least a specified capacity. * The actual capacity is calculated from the growth factor * or growth chunk specified to the constructor. * * @param capacity * the minimum capacity of this list. * * @return the new capacity of this list. * * @see #capacity() */ public int ensureCapacity(int capacity) { if (capacity > data.length) { int[] newdata = new int[capacity = computeCapacity(capacity)]; System.arraycopy(data, 0, newdata, 0, size); data = newdata; } return capacity; } /** * Returns the current capacity of this list. The capacity is the * number of elements that the list can contain without having to * increase the amount of memory used. * * @return the current capacity of this list. * * @see #ensureCapacity(int) */ public int capacity() { return data.length; } // --------------------------------------------------------------- // Operations not supported by abstract implementation // --------------------------------------------------------------- @Override public void add(int index, int v) { if (index < 0 || index > size) { Exceptions.indexOutOfBounds(index, 0, size); } ensureCapacity(size+1); // Move data int block = size-index; if (block > 0) { System.arraycopy(data, index, data, index+1, block); } data[index] = v; size++; } @Override public int get(int index) { if (index < 0 || index >= size) { Exceptions.indexOutOfBounds(index, 0, size-1); } return data[index]; } @Override public int set(int index, int v) { if (index < 0 || index >= size) { Exceptions.indexOutOfBounds(index, 0, size-1); } int result = data[index]; data[index] = v; return result; } @Override public int removeElementAt(int index) { if (index < 0 || index >= size) { Exceptions.indexOutOfBounds(index, 0, size-1); } int result = data[index]; // Move data int block = size-(index+1); if (block > 0) { System.arraycopy(data, index+1, data, index, block); } size--; return result; } /** * Minimizes the memory used by this array list. The underlying * array is replaced by an array whose size is exactly the number * of elements in this array list. The method can be used to * free up memory after many removals. */ @Override public void trimToSize() { if (data.length > size) { int[] newdata = new int[size]; System.arraycopy(data, 0, newdata, 0, size); data = newdata; } } /** * Returns a clone of this array list. * * @return a clone of this array list. * * @since 1.1 */ @Override public Object clone() { try { IntArrayList c = (IntArrayList)super.clone(); c.data = new int[data.length]; System.arraycopy(data, 0, c.data, 0, size); return c; } catch (CloneNotSupportedException e) { Exceptions.cloning(); return null; } } // --------------------------------------------------------------- // Operations overwritten for efficiency // --------------------------------------------------------------- @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public void clear() { size = 0; } @Override public boolean contains(int v) { for (int i = 0; i < size; i++) { if (data[i] == v) { return true; } } return false; } @Override public int indexOf(int c) { for (int i = 0; i < size; i++) { if (data[i] == c) { return i; } } return -1; } /** * @since 1.2 */ @Override public int indexOf(int index, int c) { if (index < 0 || index > size) { Exceptions.indexOutOfBounds(index, 0, size); } for (int i = index; i < size; i++) { if (data[i] == c) { return i; } } return -1; } @Override public int lastIndexOf(int c) { for (int i = size-1; i >= 0; i--) { if (data[i] == c) { return i; } } return -1; } @Override public boolean remove(int v) { int index = indexOf(v); if (index != -1) { removeElementAt(index); return true; } return false; } @Override public int[] toArray() { int[] a = new int[size]; System.arraycopy(data, 0, a, 0, size); return a; } @Override public int[] toArray(int[] a) { if (a == null || a.length < size) { a = new int[size]; } System.arraycopy(data, 0, a, 0, size); return a; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof IntList)) { return false; } int i1 = 0; IntListIterator i2 = ((IntList)obj).listIterator(); while(i1 < size && i2.hasNext()) { if (data[i1++] != i2.next()) { return false; } } return !(i1 < size || i2.hasNext()); } @Override public int hashCode() { int h = 1; for (int i = 0; i < size; i++) { h = 31*h + DefaultIntHashFunction.INSTANCE.hash(data[i]); } return h; } // --------------------------------------------------------------- // Serialization // --------------------------------------------------------------- /** * @serialData Default fields; the capacity of the * list (<tt>int</tt>); the list's elements * (<tt>int</tt>). * * @since 1.1 */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeInt(data.length); for (int i = 0; i < size; i++) { s.writeInt(data[i]); } } /** * @since 1.1 */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); data = new int[s.readInt()]; for (int i = 0; i < size; i++) { data[i] = s.readInt(); } } }