/* File: Dynarray.java Originally written by Doug Lea and released into the public domain. Thanks for the assistance and support of Sun Microsystems Labs, Agorics Inc, Loral, and everyone contributing, testing, and using this code. History: Date Who What 2Oct95 dl@cs.oswego.edu refactored from DASeq.java 13Oct95 dl Changed protection statuses */ package collections; import java.util.Enumeration; import java.util.NoSuchElementException; /** * * Dynamically allocated and resized Arrays. * * Beyond implementing its interfaces, adds methods * to adjust capacities. The default heuristics for resizing * usually work fine, but you can adjust them manually when * you need to. * * Dynarrays are generally like java.util.Vectors. But unlike them, * Dynarrays do not actually allocate arrays when they are constructed. * Among other consequences, you can adjust the capacity `for free' * after construction but before adding elements. You can adjust * it at other times as well, but this may lead to more expensive * resizing. Also, unlike Vectors, they release their internal arrays * whenever they are empty. * * @author Doug Lea * @version 0.93 * *
For an introduction to this package see Overview . * **/ public class Dynarray extends UpdatableSeqImpl implements UpdatableSeq, SortableCollection { /** * The minimum capacity of any non-empty buffer **/ public static final int minCapacity = 16; // instance variables /** * The elements, or null if no buffer yet allocated. **/ protected Object array_[]; // constructors /** * Make a new empty Dynarray. **/ public Dynarray() { this(null, null, 0); } /** * Make an empty Dynarray with given element screener **/ public Dynarray(Predicate screener) { this(screener, null, 0); } /** * Special version of constructor needed by clone() **/ protected Dynarray(Predicate s, Object b[], int c) { super(s); array_ = b; count_ = c; } /** * Make an independent copy. The elements themselves are not cloned **/ protected Object clone() throws CloneNotSupportedException { int cap = count_; if (cap == 0) return new Dynarray(screener_, null, 0); else { if (cap < minCapacity) cap = minCapacity; Object newArray[] = new Object[cap]; System.arraycopy(array_, 0, newArray, 0, count_); return new Dynarray(screener_, newArray, count_); } } // methods introduced in Dynarray /** * return the current internal buffer capacity (zero if no buffer allocated). * @return capacity (always greater than or equal to size()) **/ public synchronized int capacity() { return (array_ == null)? 0 : array_.length; } /** * Set the internal buffer capacity to max(size(), newCap). * That is, if given an argument less than the current * number of elements, the capacity is just set to the * current number of elements. Thus, elements are never lost * by setting the capacity. * * @param newCap the desired capacity. * @return condition: *
* capacity() >= size() && * version() != PREV(this).version() == (capacity() != PREV(this).capacity()) ***/ public synchronized void capacity(int newCap) { if (newCap < count_) newCap = count_; if (newCap == 0) { clear(); } else if (array_ == null) { array_ = new Object[newCap]; incVersion(); } else if (newCap != array_.length) { Object newArray[] = new Object[newCap]; System.arraycopy(array_, 0, newArray, 0, count_); array_ = newArray; incVersion(); } } // Collection methods /** * Implements collections.Collection.includes. * Time complexity: O(n). * @see collections.Collection#includes **/ public synchronized boolean includes(Object element) { if (element == null) return false; for (int i = 0; i < count_; ++i) if (array_[i].equals(element)) return true; return false; } /** * Implements collections.Collection.occurrencesOf. * Time complexity: O(n). * @see collections.Collection#occurrencesOf **/ public synchronized int occurrencesOf(Object element) { if (element == null) return 0; int c = 0; for (int i = 0; i < count_; ++i) if (array_[i].equals(element)) ++c; return c; } /** * Implements collections.Collection.elements. * Time complexity: O(1). * @see collections.Collection#elements **/ public synchronized CollectionEnumeration elements() { return new DAEnumeration(this, array_); } // Seq methods: /** * Implements collections.Seq.first. * Time complexity: O(1). * @see collections.Seq#first **/ public synchronized Object first() throws NoSuchElementException { checkIndex(0); return array_[0]; } /** * Implements collections.Seq.last. * Time complexity: O(1). * @see collections.Seq#last **/ public synchronized Object last() throws NoSuchElementException { checkIndex(count_-1); return array_[count_-1]; } /** * Implements collections.Seq.at. * Time complexity: O(1). * @see collections.Seq#at **/ public synchronized Object at(int index) throws NoSuchElementException { checkIndex(index); return array_[index]; } /** * Implements collections.Seq.firstIndexOf. * Time complexity: O(n). * @see collections.Seq#firstIndexOf **/ public synchronized int firstIndexOf(Object element, int startingIndex) { if (startingIndex < 0) startingIndex = 0; for (int i = startingIndex; i < count_; ++i) if (array_[i].equals(element)) return i; return -1; } /** * Implements collections.Seq.lastIndexOf. * Time complexity: O(n). * @see collections.Seq#lastIndexOf **/ public synchronized int lastIndexOf(Object element, int startingIndex) { if (startingIndex >= count_) startingIndex = count_ -1; for (int i = startingIndex; i >= 0; --i) if (array_[i].equals(element)) return i; return -1; } /** * Implements collections.Seq.firstIndexOf. * Time complexity: O(n). * @see collections.Seq#firstIndexOf **/ public synchronized int firstIndexOf(Object element) { return firstIndexOf(element, 0); } /** * Implements collections.Seq.lastIndexOf. * Time complexity: O(n). * @see collections.Seq#lastIndexOf **/ public synchronized int lastIndexOf(Object element) { return lastIndexOf(element, size()-1); } /** * Implements collections.Seq.subseq. * Time complexity: O(length). * @see collections.Seq#subseq **/ public synchronized /* Dynarray */ Seq subseq(int from, int length) throws NoSuchElementException { if (length > 0) { checkIndex(from); checkIndex(from+length-1); Object newArray[] = new Object[length]; System.arraycopy(array_, from, newArray, 0, length); return new Dynarray(screener_, newArray, length); } else return new Dynarray(screener_); } // UpdatableCollection methods /** * Implements collections.UpdatableCollection.clear. * Time complexity: O(1). * @see collections.UpdatableCollection#clear **/ public synchronized void clear() { array_ = null; setCount(0); } /** * Implements collections.UpdatableCollection.removeOneOf. * Time complexity: O(n). * @see collections.UpdatableCollection#removeOneOf **/ public synchronized void removeOneOf(Object element) { remove_(element, false); } /** * Implements collections.UpdatableCollection.replaceOneOf * Time complexity: O(n). * @see collections.UpdatableCollection#replaceOneOf **/ public synchronized void replaceOneOf(Object oldElement, Object newElement) throws IllegalElementException { replace_(oldElement, newElement, false); } /** * Implements collections.UpdatableCollection.replaceAllOf. * Time complexity: O(n * number of replacements). * @see collections.UpdatableCollection#replaceAllOf **/ public synchronized void replaceAllOf(Object oldElement, Object newElement) throws IllegalElementException { replace_(oldElement, newElement, true); } /** * Implements collections.UpdatableCollection.exclude. * Time complexity: O(n * occurrencesOf(element)). * @see collections.UpdatableCollection#exclude **/ public synchronized void exclude(Object element) { remove_(element, true); } /** * Implements collections.UpdatableCollection.take. * Time complexity: O(1). * Takes the rightmost element of the array. * @see collections.UpdatableCollection#take **/ public synchronized Object take() throws NoSuchElementException { Object v = last(); removeLast(); return v; } // SortableCollection methods: /** * Implements collections.SortableCollection.sort. * Time complexity: O(n log n). * Uses a quicksort-based algorithm. * @see collections.SortableCollection#sort **/ public void sort(Comparator cmp) { if (count_ > 0) { quickSort(array_, 0, count_ - 1, cmp); incVersion(); } } // UpdatableSeq methods /** * Implements collections.UpdatableSeq.insertFirst. * Time complexity: O(n) * @see collections.UpdatableSeq#insertFirst **/ public synchronized void insertFirst(Object element) throws IllegalElementException { checkElement(element); growBy_(1); for (int i = count_-1; i > 0; --i) array_[i] = array_[i-1]; array_[0] = element; } /** * Implements collections.UpdatableSeq.replaceFirst. * Time complexity: O(1). * @see collections.UpdatableSeq#replaceFirst **/ public synchronized void replaceFirst(Object element) throws IllegalElementException { checkElement(element); array_[0] = element; incVersion(); } /** * Implements collections.UpdatableSeq.removeFirst. * Time complexity: O(n). * @see collections.UpdatableSeq#removeFirst **/ public synchronized void removeFirst() throws NoSuchElementException { removeAt(0); } /** * Implements collections.UpdatableSeq.insertLast. * Time complexity: normally O(1), but O(n) if size() == capacity(). * @see collections.UpdatableSeq#insertLast **/ public synchronized void insertLast(Object element) throws IllegalElementException { checkElement(element); int last = count_; growBy_(1); array_[last] = element; } /** * Implements collections.UpdatableSeq.replaceLast. * Time complexity: O(1). * @see collections.UpdatableSeq#replaceLast **/ public synchronized void replaceLast(Object element) throws IllegalElementException, NoSuchElementException { checkElement(element); array_[count_-1] = element; incVersion(); } /** * Implements collections.UpdatableSeq.removeLast. * Time complexity: O(1). * @see collections.UpdatableSeq#removeLast **/ public synchronized void removeLast() throws NoSuchElementException { checkIndex(0); array_[count_-1] = null; growBy_(-1); } /** * Implements collections.UpdatableSeq.insertAt. * Time complexity: O(n). * @see collections.UpdatableSeq#insertAt **/ public synchronized void insertAt(int index, Object element) throws IllegalElementException, NoSuchElementException { if (index != count_) checkIndex(index); checkElement(element); growBy_(1); for (int i = count_-1; i > index; --i) array_[i] = array_[i-1]; array_[index] = element; } /** * Implements collections.UpdatableSeq.removeAt. * Time complexity: O(n). * @see collections.UpdatableSeq#removeAt **/ public synchronized void removeAt(int index) throws NoSuchElementException { checkIndex(index); for (int i = index+1; i < count_; ++i) array_[i-1] = array_[i]; array_[count_-1] = null; growBy_(-1); } /** * Implements collections.UpdatableSeq.replaceAt. * Time complexity: O(1). * @see collections.UpdatableSeq#replaceAt **/ public synchronized void replaceAt(int index, Object element) throws IllegalElementException, NoSuchElementException { checkIndex(index); checkElement(element); array_[index] = element; incVersion(); } /** * Implements collections.UpdatableSeq.prependElements. * Time complexity: O(n + number of elements in e) if (e * instanceof CollectionEnumeration) else O(n * number of elements in e) * @see collections.UpdatableSeq#prependElements **/ public synchronized void prependElements(Enumeration e) throws IllegalElementException, CorruptedEnumerationException { insertElementsAt_(0, e); } /** * Implements collections.UpdatableSeq.appendElements. * Time complexity: O(number of elements in e) * @see collections.UpdatableSeq#appendElements **/ public synchronized void appendElements(Enumeration e) throws IllegalElementException, CorruptedEnumerationException { insertElementsAt_(count_, e); } /** * Implements collections.UpdatableSeq.insertElementsAt. * Time complexity: O(n + number of elements in e) if (e * instanceof CollectionEnumeration) else O(n * number of elements in e) * @see collections.UpdatableSeq#insertElementsAt **/ public synchronized void insertElementsAt(int index, Enumeration e) throws IllegalElementException, CorruptedEnumerationException, NoSuchElementException { if (index != count_) checkIndex(index); insertElementsAt_(index, e); } /** * Implements collections.UpdatableSeq.removeFromTo. * Time complexity: O(n). * @see collections.UpdatableSeq#removeFromTo **/ public synchronized void removeFromTo(int fromIndex, int toIndex) throws NoSuchElementException { checkIndex(fromIndex); checkIndex(toIndex); if (fromIndex <= toIndex) { int gap = toIndex - fromIndex + 1; int j = fromIndex; for (int i = toIndex+1; i < count_; ++i) array_[j++] = array_[i]; for (int i = 1; i <= gap; ++i) array_[count_-i] = null; addToCount(-gap); } } /** * An implementation of Quicksort using medians of 3 for partitions. * Used internally by sort. * It is public and static so it can be used to sort plain * arrays as well. * @param s, the array to sort * @param lo, the least index to sort from * @param hi, the greatest index * @param cmp, the comparator to use for comparing elements **/ public static void quickSort(Object s[], int lo, int hi, Comparator cmp) { if (lo >= hi) return; /* Use median-of-three(lo, mid, hi) to pick a partition. Also swap them into relative order while we are at it. */ int mid = (lo + hi) / 2; if (cmp.compare(s[lo], s[mid]) > 0) { Object tmp = s[lo]; s[lo] = s[mid]; s[mid] = tmp; // swap } if (cmp.compare(s[mid], s[hi]) > 0) { Object tmp = s[mid]; s[mid] = s[hi]; s[hi] = tmp; // swap if (cmp.compare(s[lo], s[mid]) > 0) { Object tmp2 = s[lo]; s[lo] = s[mid]; s[mid] = tmp2; // swap } } int left = lo+1; // start one past lo since already handled lo int right = hi-1; // similarly if (left >= right) return; // if three or fewer we are done Object partition = s[mid]; for (;;) { while (cmp.compare(s[right], partition) > 0) --right; while (left < right && cmp.compare(s[left], partition) <= 0) ++left; if (left < right) { Object tmp = s[left]; s[left] = s[right]; s[right] = tmp; // swap --right; } else break; } quickSort(s, lo, left, cmp); quickSort(s, left+1, hi, cmp); } // helper methods /** * Main method to control buffer sizing. * The heuristic used for growth is: *
* if out of space: * if need less than minCapacity, grow to minCapacity * else grow by average of requested size and minCapacity. **
* For small buffers, this causes them to be about 1/2 full. * while for large buffers, it causes them to be about 2/3 full. *
* For shrinkage, the only thing we do is unlink the buffer if it is empty. * @param inc, the amount of space to grow by. Negative values mean shrink. * @return condition: adjust record of count, and if any of * the above conditions apply, allocate and copy into a new * buffer of the appropriate size. **/ private void growBy_(int inc) { int needed = count_ + inc; if (inc > 0) { /* heuristic: */ int current = capacity(); if (needed > current) { incVersion(); int newCap = needed + (needed + minCapacity) / 2; if (newCap < minCapacity) newCap = minCapacity; if (array_ == null) { array_ = new Object[newCap]; } else { Object newArray[] = new Object[newCap]; System.arraycopy(array_, 0, newArray, 0, count_); array_ = newArray; } } } else if (needed == 0) array_ = null; setCount(needed); } /** * Utility to splice in enumerations **/ private void insertElementsAt_(int index, Enumeration e) throws CorruptedEnumerationException, IllegalElementException { if (e instanceof CollectionEnumeration) { // we know size! int inc = ((CollectionEnumeration)(e)).numberOfRemainingElements(); int oldcount = count_; int oldversion = version_; growBy_(inc); for (int i = oldcount-1; i >= index; --i) array_[i+inc] = array_[i]; int j = index; while (e.hasMoreElements()) { Object element = e.nextElement(); if (!canInclude(element)) { // Ugh. Can only do full rollback for (int i = index; i < oldcount; ++i) array_[i] = array_[i+inc]; version_ = oldversion; count_ = oldcount; checkElement(element); // force throw } array_[j++] = element; } } else if (index == count_) { // next best; we can append while (e.hasMoreElements()) { Object element = e.nextElement(); checkElement(element); growBy_(1); array_[count_-1] = element; } } else { // do it the slow way int j = index; while (e.hasMoreElements()) { Object element = e.nextElement(); checkElement(element); growBy_(1); for (int i = count_-1; i > j; --i) array_[i] = array_[i-1]; array_[j++] = element; } } } private void remove_(Object element, boolean allOccurrences) { if (element == null) return; for (int i = 0; i < count_; ++i) { while (i < count_ && array_[i].equals(element)) { for (int j = i+1; j < count_; ++j) array_[j-1] = array_[j]; array_[count_-1] = null; growBy_(-1); if (!allOccurrences || count_ == 0) return; } } } private void replace_(Object oldElement, Object newElement, boolean allOccurrences) throws IllegalElementException { if (oldElement == null || count_ == 0) return; for (int i = 0; i < count_; ++i) { if (array_[i].equals(oldElement)) { checkElement(newElement); array_[i] = newElement; incVersion(); if (!allOccurrences) return; } } } // ImplementationCheckable methods /** * Implements collections.ImplementationCheckable.checkImplementation. * @see collections.ImplementationCheckable#checkImplementation **/ public synchronized void checkImplementation() throws ImplementationError { super.checkImplementation(); assert(!(array_ == null && count_ != 0)); assert((array_ == null || count_ <= array_.length)); for (int i = 0; i < count_; ++i) { assert(canInclude(array_[i])); assert(occurrencesOf(array_[i]) > 0); assert(includes(array_[i])); } } }