KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > math > util > ResizableDoubleArray


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 JavaDoc;
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$ $Date: 2005-02-26 05:11:52 -0800 (Sat, 26 Feb 2005) $
70  */

71 public class ResizableDoubleArray implements DoubleArray, Serializable JavaDoc {
72     
73     /** Serializable version identifier */
74     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 JavaDoc 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 JavaDoc(msg);
318         }
319
320         if (contractionCriteria <= 1.0) {
321             String JavaDoc 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 JavaDoc(msg);
328         }
329
330         if (expansionFactor <= 1.0) {
331             String JavaDoc msg =
332                 "The expansion factor must be a number greater than 1.0";
333             throw new IllegalArgumentException JavaDoc(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 JavaDoc msg = "Cannot discard more elements than are" +
375             "contained in this array.";
376             throw new IllegalArgumentException JavaDoc(msg);
377         } else if (i < 0) {
378             String JavaDoc msg = "Cannot discard a negative number of elements.";
379             throw new IllegalArgumentException JavaDoc(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 double getElement(int index) {
455         double value = Double.NaN;
456         if (index >= numElements) {
457             String JavaDoc msg =
458                 "The index specified: " + index +
459                 " is larger than the current number of elements";
460             throw new ArrayIndexOutOfBoundsException JavaDoc(msg);
461         } else if (index >= 0) {
462             value = internalArray[startIndex + index];
463         } else {
464             String JavaDoc msg =
465                 "Elements cannot be retrieved from a negative array index";
466             throw new ArrayIndexOutOfBoundsException JavaDoc(msg);
467         }
468         return value;
469     }
470     
471      /**
472      * Returns a double array containing the elements of this
473      * <code>ResizableArray</code>. This method returns a copy, not a
474      * reference to the underlying array, so that changes made to the returned
475      * array have no effect on this <code>ResizableArray.</code>
476      * @return the double array.
477      */

478     public double[] getElements() {
479         double[] elementArray = new double[numElements];
480         System.arraycopy( internalArray, startIndex, elementArray, 0,
481                 numElements);
482         return elementArray;
483     }
484     
485     /**
486      * The expansion factor controls the size of a new aray when an array
487      * needs to be expanded. The <code>expansionMode</code>
488      * determines whether the size of the array is multiplied by the
489      * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
490      * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
491      * storage locations added). The default <code>expansionMode</code> is
492      * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
493      * is 2.0.
494      *
495      * @return the expansion factor of this expandable double array
496      */

497     public float getExpansionFactor() {
498         return expansionFactor;
499     }
500     
501     /**
502      * The <code>expansionMode</code> determines whether the internal storage
503      * array grows additively (ADDITIVE_MODE) or multiplicatively
504      * (MULTIPLICATIVE_MODE) when it is expanded.
505      *
506      * @return Returns the expansionMode.
507      */

508     public int getExpansionMode() {
509         return expansionMode;
510     }
511     
512     /**
513      * Notice the package scope on this method. This method is simply here
514      * for the JUnit test, it allows us check if the expansion is working
515      * properly after a number of expansions. This is not meant to be a part
516      * of the public interface of this class.
517      *
518      * @return the length of the internal storage array.
519      */

520     int getInternalLength() {
521         return (internalArray.length);
522     }
523
524     /**
525      * Returns the number of elements currently in the array. Please note
526      * that this is different from the length of the internal storage array.
527      *
528      * @return number of elements
529      */

530     public int getNumElements() {
531         return (numElements);
532     }
533     
534     /**
535      * Returns the internal storage array. Note that this method returns
536      * a reference to the internal storage array, not a copy, and to correctly
537      * address elements of the array, the <code>startIndex</code> is
538      * required (available via the {@link #start} method). This method should
539      * only be used in cases where copying the internal array is not practical.
540      * The {@link #getElements} method should be used in all other cases.
541      *
542      *
543      * @return the internal storage array used by this object
544      */

545     public double[] getValues() {
546         return (internalArray);
547     }
548
549     /**
550      * Sets the contraction criteria for this ExpandContractDoubleArray.
551      *
552      * @param contractionCriteria contraction criteria
553      */

554     public void setContractionCriteria(float contractionCriteria) {
555         checkContractExpand(contractionCriteria, getExpansionFactor());
556         this.contractionCriteria = contractionCriteria;
557     }
558     
559
560     /**
561      * Sets the element at the specified index. If the specified index is greater than
562      * <code>getNumElements() - 1</code>, the <code>numElements</code> property
563      * is increased to <code>index +1</code> and additional storage is allocated
564      * (if necessary) for the new element and all (uninitialized) elements
565      * between the new element and the previous end of the array).
566      *
567      * @param index index to store a value in
568      * @param value value to store at the specified index
569      * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
570      * zero.
571      */

572     public synchronized void setElement(int index, double value) {
573         if (index < 0) {
574             String JavaDoc msg = "Cannot set an element at a negative index";
575             throw new ArrayIndexOutOfBoundsException JavaDoc(msg);
576         }
577         if (index + 1 > numElements) {
578             numElements = index + 1;
579         }
580         if ((startIndex + index) >= internalArray.length) {
581             expandTo(startIndex + (index + 1));
582         }
583         internalArray[startIndex + index] = value;
584     }
585
586     /**
587      * Sets the expansionFactor. Throws IllegalArgumentException if the
588      * the following conditions are not met:
589      * <ul>
590      * <li><code>expansionFactor > 1</code></li>
591      * <li><code>contractionFactor >= expansionFactor</code></li>
592      * </ul>
593      * @param expansionFactor the new expansion factor value.
594      * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
595      * than contractionFactor
596      */

597     public void setExpansionFactor(float expansionFactor) {
598         checkContractExpand(getContractionCriteria(), expansionFactor);
599         // The check above verifies that the expansion factor is > 1.0;
600
this.expansionFactor = expansionFactor;
601     }
602
603     /**
604      * Sets the <code>expansionMode</code>. The specified value must be one of
605      * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
606      *
607      * @param expansionMode The expansionMode to set.
608      * @throws IllegalArgumentException if the specified mode value is not valid
609      */

610     public void setExpansionMode(int expansionMode) {
611         if (expansionMode != MULTIPLICATIVE_MODE &&
612                 expansionMode != ADDITIVE_MODE) {
613             throw new IllegalArgumentException JavaDoc("Illegal expansionMode setting.");
614         }
615         this.expansionMode = expansionMode;
616     }
617     
618     /**
619      * Sets the initial capacity. Should only be invoked by constructors.
620      *
621      * @param initialCapacity of the array
622      * @throws IllegalArgumentException if <code>initialCapacity</code> is not
623      * positive.
624      */

625     protected void setInitialCapacity(int initialCapacity) {
626         if (initialCapacity > 0) {
627             this.initialCapacity = initialCapacity;
628         } else {
629             String JavaDoc msg =
630                 "The initial capacity supplied: " + initialCapacity +
631                 "must be a positive integer";
632             throw new IllegalArgumentException JavaDoc(msg);
633         }
634     }
635     
636     /**
637      * This function allows you to control the number of elements contained
638      * in this array, and can be used to "throw out" the last n values in an
639      * array. This function will also expand the internal array as needed.
640      *
641      * @param i a new number of elements
642      * @throws IllegalArgumentException if <code>i</code> is negative.
643      */

644     public synchronized void setNumElements(int i) {
645
646         // If index is negative thrown an error
647
if (i < 0) {
648             String JavaDoc msg =
649                 "Number of elements must be zero or a positive " + "integer";
650             throw new IllegalArgumentException JavaDoc(msg);
651         }
652
653         // Test the new num elements, check to see if the array needs to be
654
// expanded to accomodate this new number of elements
655
if ((startIndex + i) > internalArray.length) {
656             expandTo(startIndex + i);
657         }
658
659         // Set the new number of elements to new value
660
numElements = i;
661     }
662
663     /**
664      * Returns true if the internal storage array has too many unused
665      * storage positions.
666      *
667      * @return true if array satisfies the contraction criteria
668      */

669     private synchronized boolean shouldContract() {
670         if (expansionMode == MULTIPLICATIVE_MODE) {
671             return (internalArray.length / numElements) > contractionCriteria;
672         } else {
673             return (internalArray.length - numElements) > contractionCriteria;
674         }
675     }
676
677     /**
678      * Returns the starting index of the internal array. The starting index is
679      * the position of the first addressable element in the internal storage
680      * array. The addressable elements in the array are <code>
681      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
682      * </code>
683      *
684      * @return starting index
685      */

686     public int start() {
687         return startIndex;
688     }
689
690 }
691
Popular Tags