View Javadoc

    1   /*
    2    * Copyright 2003-2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.math.util;
    17
    18  import java.io.Serializable;
    19
    20  /***
    21   * <p>
    22   * A variable length {@link DoubleArray} implementation that automatically
    23   * handles expanding and contracting its internal storage array as elements
    24   * are added and removed.
    25   * </p>
    26   * <p>
    27   *  The internal storage array starts with capacity determined by the
    28   * <code>initialCapacity</code> property, which can be set by the constructor.
    29   * The default initial capacity is 16.  Adding elements using
    30   * {@link #addElement(double)} appends elements to the end of the array.  When
    31   * there are no open entries at the end of the internal storage array, the
    32   * array is expanded.  The size of the expanded array depends on the
    33   * <code>expansionMode</code> and <code>expansionFactor</code> properties.
    34   * The <code>expansionMode</code> determines whether the size of the array is
    35   * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
    36   * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
    37   * storage locations added).  The default <code>expansionMode</code> is
    38   * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
    39   * is 2.0.
    40   * </p>
    41   * <p>
    42   * The {@link #addElementRolling(double)} method adds a new element to the end
    43   * of the internal storage array and adjusts the "usable window" of the
    44   * internal array forward by one position (effectively making what was the
    45   * second element the first, and so on).  Repeated activations of this method
    46   * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
    47   * the storage locations at the beginning of the internal storage array.  To
    48   * reclaim this storage, each time one of these methods is activated, the size
    49   * of the internal storage array is compared to the number of addressable
    50   * elements (the <code>numElements</code> property) and if the difference
    51   * is too large, the internal array is contracted to size
    52   * <code>numElements + 1.</code>  The determination of when the internal
    53   * storage array is "too large" depends on the <code>expansionMode</code> and
    54   * <code>contractionFactor</code> properties.  If  the <code>expansionMode</code>
    55   * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the
    56   * ratio between storage array length and <code>numElements</code> exceeds
    57   * <code>contractionFactor.</code>  If the <code>expansionMode</code>
    58   * is <code>ADDITIVE_MODE,</code> the number of excess storage locations
    59   * is compared to <code>contractionFactor.</code>
    60   * </p>
    61   * <p>
    62   * To avoid cycles of expansions and contractions, the
    63   * <code>expansionFactor</code> must not exceed the
    64   * <code>contractionFactor.</code> Constructors and mutators for both of these
    65   * properties enforce this requirement, throwing IllegalArgumentException if it
    66   * is violated.
    67   * </p>
    68   * <p>
    69   * @version $Revision: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $
    70   */
    71  public class ResizableDoubleArray implements DoubleArray, Serializable {
    72
    73      /*** Serializable version identifier */
    74      private static final long serialVersionUID = -3485529955529426875L;
    75
    76      /*** additive expansion mode */
    77      public static final int ADDITIVE_MODE = 1;
    78
    79      /*** multiplicative expansion mode */
    80      public static final int MULTIPLICATIVE_MODE = 0;
    81
    82      /***
    83       * The contraction criteria determines when the internal array will be
    84       * contracted to fit the number of elements contained in the element
    85       *  array + 1.
    86       */
    87      protected float contractionCriteria = 2.5f;
    88
    89      /***
    90       * The expansion factor of the array.  When the array needs to be expanded,
    91       * the new array size will be
    92       * <code>internalArray.length * expansionFactor</code>
    93       * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
    94       * <code>internalArray.length + expansionFactor</code> if
    95       * <code>expansionMode</code> is set to ADDITIVE_MODE.
    96       */
    97      protected float expansionFactor = 2.0f;
    98
    99      /***
    100      * Determines whether array expansion by <code>expansionFactor</code>
    101      * is additive or multiplicative.
    102      */
    103     protected int expansionMode = MULTIPLICATIVE_MODE;
    104
    105     /***
    106      * The initial capacity of the array.  Initial capacity is not exposed as a
    107      * property as it is only meaningful when passed to a constructor.
    108      */
    109     protected int initialCapacity = 16;
    110
    111     /***
    112      * The internal storage array.
    113      */
    114     protected double[] internalArray;
    115
    116     /***
    117      * The number of addressable elements in the array.  Note that this
    118      * has nothing to do with the length of the internal storage array.
    119      */
    120     protected int numElements = 0;
    121
    122     /***
    123      * The position of the first addressable element in the internal storage
    124      * array.  The addressable elements in the array are <code>
    125      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
    126      * </code>
    127      */
    128     protected int startIndex = 0;
    129
    130     /***
    131      * Create a ResizableArray with default properties.
    132      * <ul>
    133      * <li><code>initialCapacity = 16</code></li>
    134      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    135      * <li><code>expansionFactor = 2.5</code></li>
    136      * <li><code>contractionFactor = 2.0</code></li>
    137      * </ul>
    138      */
    139     public ResizableDoubleArray() {
    140         internalArray = new double[initialCapacity];
    141     }
    142
    143     /***
    144      * Create a ResizableArray with the specified initial capacity.  Other
    145      * properties take default values:
    146       * <ul>
    147      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    148      * <li><code>expansionFactor = 2.5</code></li>
    149      * <li><code>contractionFactor = 2.0</code></li>
    150      * </ul>
    151      * @param initialCapacity The initial size of the internal storage array
    152      * @throws IllegalArgumentException if initialCapacity is not > 0
    153      */
    154     public ResizableDoubleArray(int initialCapacity) {
    155         setInitialCapacity(initialCapacity);
    156         internalArray = new double[this.initialCapacity];
    157     }
    158
    159     /***
    160      * <p>
    161      * Create a ResizableArray with the specified initial capacity
    162      * and expansion factor.  The remaining properties take default
    163      * values:
    164      * <ul>
    165      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    166      * <li><code>contractionFactor = 0.5 + expansionFactor</code></li>
    167      * </ul></p>
    168      * <p>
    169      * Throws IllegalArgumentException if the following conditions are
    170      * not met:
    171      * <ul>
    172      * <li><code>initialCapacity > 0</code></li>
    173      * <li><code>expansionFactor > 1</code></li>
    174      * </ul></p>
    175      *
    176      * @param initialCapacity The initial size of the internal storage array
    177      * @param expansionFactor the array will be expanded based on this
    178      *                        parameter
    179      * @throws IllegalArgumentException if parameters are not valid
    180      */
    181     public ResizableDoubleArray(int initialCapacity, float expansionFactor) {
    182         this.expansionFactor = expansionFactor;
    183         setInitialCapacity(initialCapacity);
    184         internalArray = new double[initialCapacity];
    185         setContractionCriteria(expansionFactor +0.5f);
    186     }
    187
    188     /***
    189      * <p>
    190      * Create a ResizableArray with the specified initialCapacity,
    191      * expansionFactor, and contractionCriteria. The <code>expansionMode</code>
    192      * will default to <code>MULTIPLICATIVE_MODE.</code></p>
    193      * <p>
    194      * Throws IllegalArgumentException if the following conditions are
    195      * not met:
    196      * <ul>
    197      * <li><code>initialCapacity > 0</code></li>
    198      * <li><code>expansionFactor > 1</code></li>
    199      * <li><code>contractionFactor >= expansionFactor</code></li>
    200      * </ul></p>
    201      * @param initialCapacity The initial size of the internal storage array
    202      * @param expansionFactor the array will be expanded based on this
    203      *                        parameter
    204      * @param contractionCriteria The contraction Criteria.
    205      * @throws IllegalArgumentException if parameters are not valid
    206      */
    207     public ResizableDoubleArray(int initialCapacity, float expansionFactor,
    208         float contractionCriteria) {
    209         this.expansionFactor = expansionFactor;
    210         setContractionCriteria(contractionCriteria);
    211         setInitialCapacity(initialCapacity);
    212         internalArray = new double[initialCapacity];
    213     }
    214
    215     /***
    216      * <p>
    217      * Create a ResizableArray with the specified properties.</p>
    218     * <p>
    219      * Throws IllegalArgumentException if the following conditions are
    220      * not met:
    221      * <ul>
    222      * <li><code>initialCapacity > 0</code></li>
    223      * <li><code>expansionFactor > 1</code></li>
    224      * <li><code>contractionFactor >= expansionFactor</code></li>
    225      * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
    226      * </li>
    227      * </ul></p>
    228      *
    229      * @param initialCapacity the initial size of the internal storage array
    230      * @param expansionFactor the array will be expanded based on this
    231      *                        parameter
    232      * @param contractionCriteria the contraction Criteria
    233      * @param expansionMode  the expansion mode
    234      * @throws IllegalArgumentException if parameters are not valid
    235      */
    236     public ResizableDoubleArray(int initialCapacity, float expansionFactor,
    237             float contractionCriteria, int expansionMode) {
    238         this.expansionFactor = expansionFactor;
    239         setContractionCriteria(contractionCriteria);
    240         setInitialCapacity(initialCapacity);
    241         setExpansionMode(expansionMode);
    242         internalArray = new double[initialCapacity];
    243     }
    244
    245     /***
    246      * Adds an element to the end of this expandable array.
    247      *
    248      * @param value to be added to end of array
    249      */
    250     public synchronized void addElement(double value) {
    251         numElements++;
    252         if ((startIndex + numElements) > internalArray.length) {
    253             expand();
    254         }
    255         internalArray[startIndex + (numElements - 1)] = value;
    256         if (shouldContract()) {
    257             contract();
    258         }
    259     }
    260
    261     /***
    262      * <p>
    263      * Adds an element to the end of the array and removes the first
    264      * element in the array.  Returns the discarded first element.
    265      * The effect is similar to a push operation in a FIFO queue.
    266      * </p>
    267      * <p>
    268      * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
    269      * and addElementRolling(5) is invoked, the result is an array containing
    270      * the entries 2, 3, 4, 5 and the value returned is 1.
    271      * </p>
    272      *
    273      * @param value the value to be added to the array
    274      * @return the value which has been discarded or "pushed" out of the array
    275      *         by this rolling insert
    276      */
    277     public synchronized double addElementRolling(double value) {
    278         double discarded = internalArray[startIndex];
    279
    280         if ((startIndex + (numElements + 1)) > internalArray.length) {
    281             expand();
    282         }
    283         // Increment the start index
    284         startIndex += 1;
    285
    286         // Add the new value
    287         internalArray[startIndex + (numElements - 1)] = value;
    288
    289         // Check the contraction criteria
    290         if (shouldContract()) {
    291             contract();
    292         }
    293         return discarded;
    294     }
    295
    296     /***
    297      * Checks the expansion factor and the contraction criteria and throws an
    298      * IllegalArgumentException if the contractionCriteria is less than the
    299      * expansionCriteria
    300      *
    301      * @param expansionFactor factor to be checked
    302      * @param contractionCritera critera to be checked
    303      * @throws IllegalArgumentException if the contractionCriteria is less than
    304      *         the expansionCriteria.
    305      */
    306     protected void checkContractExpand(
    307         float contractionCritera,
    308         float expansionFactor) {
    309
    310         if (contractionCritera < expansionFactor) {
    311             String msg =
    312                 "Contraction criteria can never be smaller than " +
    313                 "the expansion factor.  This would lead to a never " +
    314                 "ending loop of expansion and contraction as a newly " +
    315                 "expanded internal storage array would immediately " +
    316                 "satisfy the criteria for contraction";
    317             throw new IllegalArgumentException(msg);
    318         }
    319
    320         if (contractionCriteria <= 1.0) {
    321             String msg =
    322                 "The contraction criteria must be a number larger " +
    323                 "than one.  If the contractionCriteria is less than or " +
    324                 "equal to one an endless loop of contraction and " +
    325                 "expansion would ensue as an internalArray.length " +
    326                 "== numElements would satisfy the contraction criteria";
    327             throw new IllegalArgumentException(msg);
    328         }
    329
    330         if (expansionFactor <= 1.0) {
    331             String msg =
    332                 "The expansion factor must be a number greater than 1.0";
    333             throw new IllegalArgumentException(msg);
    334         }
    335     }
    336
    337     /***
    338      * Clear the array, reset the size to the initialCapacity and the number
    339      * of elements to zero.
    340      */
    341     public synchronized void clear() {
    342         numElements = 0;
    343         internalArray = new double[initialCapacity];
    344     }
    345
    346     /***
    347      * Contracts the storage array to the (size of the element set) + 1 - to
    348      * avoid a zero length array. This function also resets the startIndex to
    349      * zero.
    350      */
    351     public synchronized void contract() {
    352         double[] tempArray = new double[numElements + 1];
    353
    354         // Copy and swap - copy only the element array from the src array.
    355         System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
    356         internalArray = tempArray;
    357
    358         // Reset the start index to zero
    359         startIndex = 0;
    360     }
    361
    362     /***
    363      * Discards the <code>i<code> initial elements of the array.  For example,
    364      * if the array contains the elements 1,2,3,4, invoking
    365      * <code>discardFrontElements(2)</code> will cause the first two elements
    366      * to be discarded, leaving 3,4 in the array.  Throws illegalArgumentException
    367      * if i exceeds numElements.
    368      *
    369      * @param i  the number of elements to discard from the front of the array
    370      * @throws IllegalArgumentException if i is greater than numElements.
    371      */
    372     public synchronized void discardFrontElements(int i) {
    373         if (i > numElements) {
    374             String msg = "Cannot discard more elements than are" +
    375             "contained in this array.";
    376             throw new IllegalArgumentException(msg);
    377         } else if (i < 0) {
    378             String msg = "Cannot discard a negative number of elements.";
    379             throw new IllegalArgumentException(msg);
    380         } else {
    381             // "Subtract" this number of discarded from numElements
    382             numElements -= i;
    383             startIndex += i;
    384         }
    385         if (shouldContract()) {
    386             contract();
    387         }
    388     }
    389
    390     /***
    391      * Expands the internal storage array using the expansion factor.
    392      * <p>
    393      * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE,
    394      * the new array size will be <code>internalArray.length * expansionFactor.</code>
    395      * If <code>expansionMode</code> is set to ADDITIVE_MODE,  the length
    396      * after expansion will be <code>internalArray.length + expansionFactor</code>
    397      */
    398     protected synchronized void expand() {
    399
    400         // notice the use of Math.ceil(), this gaurantees that we will always
    401         // have an array of at least currentSize + 1.   Assume that the
    402         // current initial capacity is 1 and the expansion factor
    403         // is 1.000000000000000001.  The newly calculated size will be
    404         // rounded up to 2 after the multiplication is performed.
    405         int newSize = 0;
    406         if (expansionMode == MULTIPLICATIVE_MODE) {
    407             newSize = (int) Math.ceil(internalArray.length * expansionFactor);
    408         } else {
    409             newSize = internalArray.length + Math.round(expansionFactor);
    410         }
    411         double[] tempArray = new double[newSize];
    412
    413         // Copy and swap
    414         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
    415         internalArray = tempArray;
    416     }
    417
    418     /***
    419      * Expands the internal storage array to the specified size.
    420      *
    421      * @param size Size of the new internal storage array
    422      */
    423     private synchronized void expandTo(int size) {
    424         double[] tempArray = new double[size];
    425         // Copy and swap
    426         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
    427         internalArray = tempArray;
    428     }
    429
    430     /***
    431      * The contraction criteria defines when the internal array will contract
    432      * to store only the number of elements in the element array.
    433      * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
    434      * contraction is triggered when the ratio between storage array length
    435      * and <code>numElements</code> exceeds <code>contractionFactor</code>.
    436      * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
    437      * number of excess storage locations is compared to
    438      * <code>contractionFactor.</code>
    439      *
    440      * @return the contraction criteria used to reclaim memory.
    441      */
    442     public float getContractionCriteria() {
    443         return contractionCriteria;
    444     }
    445
    446     /***
    447      * Returns the element at the specified index
    448      *
    449      * @param index index to fetch a value from
    450      * @return value stored at the specified index
    451      * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
    452      *         zero or is greater than <code>getNumElements() - 1</code>.
    453      */
    454     public synchronized double getElement(int index) {
    455         if (index >= numElements) {
    456             String msg =
    457                 "The index specified: " + index +
    458                 " is larger than the current number of elements";
    459             throw new ArrayIndexOutOfBoundsException(msg);
    460         } else if (index >= 0) {
    461             return internalArray[startIndex + index];
    462         } else {
    463             String msg =
    464                 "Elements cannot be retrieved from a negative array index";
    465             throw new ArrayIndexOutOfBoundsException(msg);
    466         }
    467     }
    468
    469      /***
    470      * Returns a double array containing the elements of this
    471      * <code>ResizableArray</code>.  This method returns a copy, not a
    472      * reference to the underlying array, so that changes made to the returned
    473      *  array have no effect on this <code>ResizableArray.</code>
    474      * @return the double array.
    475      */
    476     public synchronized double[] getElements() {
    477         double[] elementArray = new double[numElements];
    478         System.arraycopy( internalArray, startIndex, elementArray, 0,
    479                 numElements);
    480         return elementArray;
    481     }
    482
    483     /***
    484      * The expansion factor controls the size of a new aray when an array
    485      * needs to be expanded.  The <code>expansionMode</code>
    486      * determines whether the size of the array is multiplied by the
    487      * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
    488      * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
    489      * storage locations added).  The default <code>expansionMode</code> is
    490      * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
    491      * is 2.0.
    492      *
    493      * @return the expansion factor of this expandable double array
    494      */
    495     public float getExpansionFactor() {
    496         return expansionFactor;
    497     }
    498
    499     /***
    500      * The <code>expansionMode</code> determines whether the internal storage
    501      * array grows additively (ADDITIVE_MODE) or multiplicatively
    502      * (MULTIPLICATIVE_MODE) when it is expanded.
    503      *
    504      * @return Returns the expansionMode.
    505      */
    506     public int getExpansionMode() {
    507         return expansionMode;
    508     }
    509
    510     /***
    511      * Notice the package scope on this method.   This method is simply here
    512      * for the JUnit test, it allows us check if the expansion is working
    513      * properly after a number of expansions.  This is not meant to be a part
    514      * of the public interface of this class.
    515      *
    516      * @return the length of the internal storage array.
    517      */
    518     synchronized int getInternalLength() {
    519         return (internalArray.length);
    520     }
    521
    522     /***
    523      * Returns the number of elements currently in the array.  Please note
    524      * that this is different from the length of the internal storage array.
    525      *
    526      * @return number of elements
    527      */
    528     public synchronized int getNumElements() {
    529         return (numElements);
    530     }
    531
    532     /***
    533      * Returns the internal storage array.  Note that this method returns
    534      * a reference to the internal storage array, not a copy, and to correctly
    535      * address elements of the array, the <code>startIndex</code> is
    536      * required (available via the {@link #start} method).  This method should
    537      * only be used in cases where copying the internal array is not practical.
    538      * The {@link #getElements} method should be used in all other cases.
    539      *
    540      *
    541      * @return the internal storage array used by this object
    542      */
    543     public synchronized double[] getValues() {
    544         return (internalArray);
    545     }
    546
    547     /***
    548      * Sets the contraction criteria for this ExpandContractDoubleArray.
    549      *
    550      * @param contractionCriteria contraction criteria
    551      */
    552     public void setContractionCriteria(float contractionCriteria) {
    553         checkContractExpand(contractionCriteria, getExpansionFactor());
    554         this.contractionCriteria = contractionCriteria;
    555     }
    556
    557
    558     /***
    559      * Sets the element at the specified index.  If the specified index is greater than
    560      * <code>getNumElements() - 1</code>, the <code>numElements</code> property
    561      * is increased to <code>index +1</code> and additional storage is allocated
    562      * (if necessary) for the new element and all  (uninitialized) elements
    563      * between the new element and the previous end of the array).
    564      *
    565      * @param index index to store a value in
    566      * @param value value to store at the specified index
    567      * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
    568      *         zero.
    569      */
    570     public synchronized void setElement(int index, double value) {
    571         if (index < 0) {
    572             String msg = "Cannot set an element at a negative index";
    573             throw new ArrayIndexOutOfBoundsException(msg);
    574         }
    575         if (index + 1 > numElements) {
    576             numElements = index + 1;
    577         }
    578         if ((startIndex + index) >= internalArray.length) {
    579             expandTo(startIndex + (index + 1));
    580         }
    581         internalArray[startIndex + index] = value;
    582     }
    583
    584     /***
    585      * Sets the expansionFactor.  Throws IllegalArgumentException if the
    586      * the following conditions are not met:
    587      * <ul>
    588      * <li><code>expansionFactor > 1</code></li>
    589      * <li><code>contractionFactor >= expansionFactor</code></li>
    590      * </ul>
    591      * @param expansionFactor the new expansion factor value.
    592      * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
    593      * than contractionFactor
    594      */
    595     public void setExpansionFactor(float expansionFactor) {
    596         checkContractExpand(getContractionCriteria(), expansionFactor);
    597         // The check above verifies that the expansion factor is > 1.0;
    598         this.expansionFactor = expansionFactor;
    599     }
    600
    601     /***
    602      * Sets the <code>expansionMode</code>. The specified value must be one of
    603      * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
    604      *
    605      * @param expansionMode The expansionMode to set.
    606      * @throws IllegalArgumentException if the specified mode value is not valid
    607      */
    608     public void setExpansionMode(int expansionMode) {
    609         if (expansionMode != MULTIPLICATIVE_MODE &&
    610                 expansionMode != ADDITIVE_MODE) {
    611             throw new IllegalArgumentException("Illegal expansionMode setting.");
    612         }
    613         this.expansionMode = expansionMode;
    614     }
    615
    616     /***
    617      * Sets the initial capacity.  Should only be invoked by constructors.
    618      *
    619      * @param initialCapacity of the array
    620      * @throws IllegalArgumentException if <code>initialCapacity</code> is not
    621      *         positive.
    622      */
    623     protected void setInitialCapacity(int initialCapacity) {
    624         if (initialCapacity > 0) {
    625             this.initialCapacity = initialCapacity;
    626         } else {
    627             String msg =
    628                 "The initial capacity supplied: " + initialCapacity +
    629                 "must be a positive integer";
    630             throw new IllegalArgumentException(msg);
    631         }
    632     }
    633
    634     /***
    635      * This function allows you to control the number of elements contained
    636      * in this array, and can be used to "throw out" the last n values in an
    637      * array. This function will also expand the internal array as needed.
    638      *
    639      * @param i a new number of elements
    640      * @throws IllegalArgumentException if <code>i</code> is negative.
    641      */
    642     public synchronized void setNumElements(int i) {
    643
    644         // If index is negative thrown an error
    645         if (i < 0) {
    646             String msg =
    647                 "Number of elements must be zero or a positive " + "integer";
    648             throw new IllegalArgumentException(msg);
    649         }
    650
    651         // Test the new num elements, check to see if the array needs to be
    652         // expanded to accomodate this new number of elements
    653         if ((startIndex + i) > internalArray.length) {
    654             expandTo(startIndex + i);
    655         }
    656
    657         // Set the new number of elements to new value
    658         numElements = i;
    659     }
    660
    661     /***
    662      * Returns true if the internal storage array has too many unused
    663      * storage positions.
    664      *
    665      * @return true if array satisfies the contraction criteria
    666      */
    667     private synchronized boolean shouldContract() {
    668         if (expansionMode == MULTIPLICATIVE_MODE) {
    669             return (internalArray.length / numElements) > contractionCriteria;
    670         } else {
    671             return (internalArray.length - numElements) > contractionCriteria;
    672         }
    673     }
    674
    675     /***
    676      * Returns the starting index of the internal array.  The starting index is
    677      * the position of the first addressable element in the internal storage
    678      * array.  The addressable elements in the array are <code>
    679      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
    680      * </code>
    681      *
    682      * @return starting index
    683      */
    684     public synchronized int start() {
    685         return startIndex;
    686     }
    687
    688 }