Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
ResizableDoubleArray |
|
| 2.1379310344827585;2.138 |
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$ $Date: 2005-02-26 05:11:52 -0800 (Sat, 26 Feb 2005) $ |
|
70 | */ |
|
71 | public class ResizableDoubleArray implements DoubleArray, Serializable { |
|
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 | 202 | 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 | 202 | protected float expansionFactor = 2.0f; |
98 | ||
99 | /** |
|
100 | * Determines whether array expansion by <code>expansionFactor</code> |
|
101 | * is additive or multiplicative. |
|
102 | */ |
|
103 | 202 | 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 | 202 | 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 | 202 | 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 | 202 | 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 | 176 | public ResizableDoubleArray() { |
140 | 176 | internalArray = new double[initialCapacity]; |
141 | 176 | } |
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 | 6 | public ResizableDoubleArray(int initialCapacity) { |
155 | 6 | setInitialCapacity(initialCapacity); |
156 | 4 | internalArray = new double[this.initialCapacity]; |
157 | 4 | } |
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 | 6 | public ResizableDoubleArray(int initialCapacity, float expansionFactor) { |
182 | 6 | this.expansionFactor = expansionFactor; |
183 | 6 | setInitialCapacity(initialCapacity); |
184 | 6 | internalArray = new double[initialCapacity]; |
185 | 6 | setContractionCriteria(expansionFactor +0.5f); |
186 | 4 | } |
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 | 6 | float contractionCriteria) { |
209 | 6 | this.expansionFactor = expansionFactor; |
210 | 6 | setContractionCriteria(contractionCriteria); |
211 | 4 | setInitialCapacity(initialCapacity); |
212 | 4 | internalArray = new double[initialCapacity]; |
213 | 4 | } |
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 | 8 | float contractionCriteria, int expansionMode) { |
238 | 8 | this.expansionFactor = expansionFactor; |
239 | 8 | setContractionCriteria(contractionCriteria); |
240 | 8 | setInitialCapacity(initialCapacity); |
241 | 8 | setExpansionMode(expansionMode); |
242 | 6 | internalArray = new double[initialCapacity]; |
243 | 6 | } |
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 | 18735 | numElements++; |
252 | 18735 | if ((startIndex + numElements) > internalArray.length) { |
253 | 424 | expand(); |
254 | } |
|
255 | 18735 | internalArray[startIndex + (numElements - 1)] = value; |
256 | 18735 | if (shouldContract()) { |
257 | 168 | contract(); |
258 | } |
|
259 | 18735 | } |
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 | 2090 | double discarded = internalArray[startIndex]; |
279 | ||
280 | 2090 | if ((startIndex + (numElements + 1)) > internalArray.length) { |
281 | 462 | expand(); |
282 | } |
|
283 | // Increment the start index |
|
284 | 2090 | startIndex += 1; |
285 | ||
286 | // Add the new value |
|
287 | 2090 | internalArray[startIndex + (numElements - 1)] = value; |
288 | ||
289 | // Check the contraction criteria |
|
290 | 2090 | if (shouldContract()) { |
291 | 228 | contract(); |
292 | } |
|
293 | 2090 | 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 | 26 | if (contractionCritera < expansionFactor) { |
311 | 4 | 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 | 4 | throw new IllegalArgumentException(msg); |
318 | } |
|
319 | ||
320 | 22 | if (contractionCriteria <= 1.0) { |
321 | 0 | 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 | 0 | throw new IllegalArgumentException(msg); |
328 | } |
|
329 | ||
330 | 22 | if (expansionFactor <= 1.0) { |
331 | 2 | String msg = |
332 | "The expansion factor must be a number greater than 1.0"; |
|
333 | 2 | throw new IllegalArgumentException(msg); |
334 | } |
|
335 | 20 | } |
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 | 40 | numElements = 0; |
343 | 40 | internalArray = new double[initialCapacity]; |
344 | 40 | } |
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 | 398 | double[] tempArray = new double[numElements + 1]; |
353 | ||
354 | // Copy and swap - copy only the element array from the src array. |
|
355 | 398 | System.arraycopy(internalArray, startIndex, tempArray, 0, numElements); |
356 | 398 | internalArray = tempArray; |
357 | ||
358 | // Reset the start index to zero |
|
359 | 398 | startIndex = 0; |
360 | 398 | } |
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 | 8 | if (i > numElements) { |
374 | 2 | String msg = "Cannot discard more elements than are" + |
375 | "contained in this array."; |
|
376 | 2 | throw new IllegalArgumentException(msg); |
377 | 6 | } else if (i < 0) { |
378 | 2 | String msg = "Cannot discard a negative number of elements."; |
379 | 2 | throw new IllegalArgumentException(msg); |
380 | } else { |
|
381 | // "Subtract" this number of discarded from numElements |
|
382 | 4 | numElements -= i; |
383 | 4 | startIndex += i; |
384 | } |
|
385 | 4 | if (shouldContract()) { |
386 | 2 | contract(); |
387 | } |
|
388 | 4 | } |
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 | 886 | int newSize = 0; |
406 | 886 | if (expansionMode == MULTIPLICATIVE_MODE) { |
407 | 880 | newSize = (int) Math.ceil(internalArray.length * expansionFactor); |
408 | } else { |
|
409 | 6 | newSize = internalArray.length + Math.round(expansionFactor); |
410 | } |
|
411 | 886 | double[] tempArray = new double[newSize]; |
412 | ||
413 | // Copy and swap |
|
414 | 886 | System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); |
415 | 886 | internalArray = tempArray; |
416 | 886 | } |
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 | 6 | double[] tempArray = new double[size]; |
425 | // Copy and swap |
|
426 | 6 | System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length); |
427 | 6 | internalArray = tempArray; |
428 | 6 | } |
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 | 16 | 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 | 46 | double value = Double.NaN; |
456 | 46 | if (index >= numElements) { |
457 | 2 | String msg = |
458 | "The index specified: " + index + |
|
459 | " is larger than the current number of elements"; |
|
460 | 2 | throw new ArrayIndexOutOfBoundsException(msg); |
461 | 44 | } else if (index >= 0) { |
462 | 42 | value = internalArray[startIndex + index]; |
463 | } else { |
|
464 | 2 | String msg = |
465 | "Elements cannot be retrieved from a negative array index"; |
|
466 | 2 | throw new ArrayIndexOutOfBoundsException(msg); |
467 | } |
|
468 | 42 | 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 | 108 | double[] elementArray = new double[numElements]; |
480 | 108 | System.arraycopy( internalArray, startIndex, elementArray, 0, |
481 | numElements); |
|
482 | 108 | 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 | 36 | 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 | 10 | 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 | 32 | 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 | 2168 | 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 | 618 | 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 | 22 | checkContractExpand(contractionCriteria, getExpansionFactor()); |
556 | 18 | this.contractionCriteria = contractionCriteria; |
557 | 18 | } |
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 | 12 | if (index < 0) { |
574 | 2 | String msg = "Cannot set an element at a negative index"; |
575 | 2 | throw new ArrayIndexOutOfBoundsException(msg); |
576 | } |
|
577 | 10 | if (index + 1 > numElements) { |
578 | 6 | numElements = index + 1; |
579 | } |
|
580 | 10 | if ((startIndex + index) >= internalArray.length) { |
581 | 4 | expandTo(startIndex + (index + 1)); |
582 | } |
|
583 | 10 | internalArray[startIndex + index] = value; |
584 | 10 | } |
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 | 4 | checkContractExpand(getContractionCriteria(), expansionFactor); |
599 | // The check above verifies that the expansion factor is > 1.0; |
|
600 | 2 | this.expansionFactor = expansionFactor; |
601 | 2 | } |
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 | 12 | if (expansionMode != MULTIPLICATIVE_MODE && |
612 | expansionMode != ADDITIVE_MODE) { |
|
613 | 4 | throw new IllegalArgumentException("Illegal expansionMode setting."); |
614 | } |
|
615 | 8 | this.expansionMode = expansionMode; |
616 | 8 | } |
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 | 24 | if (initialCapacity > 0) { |
627 | 22 | this.initialCapacity = initialCapacity; |
628 | } else { |
|
629 | 2 | String msg = |
630 | "The initial capacity supplied: " + initialCapacity + |
|
631 | "must be a positive integer"; |
|
632 | 2 | throw new IllegalArgumentException(msg); |
633 | } |
|
634 | 22 | } |
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 | 6 | if (i < 0) { |
648 | 2 | String msg = |
649 | "Number of elements must be zero or a positive " + "integer"; |
|
650 | 2 | throw new IllegalArgumentException(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 | 4 | if ((startIndex + i) > internalArray.length) { |
656 | 2 | expandTo(startIndex + i); |
657 | } |
|
658 | ||
659 | // Set the new number of elements to new value |
|
660 | 4 | numElements = i; |
661 | 4 | } |
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 | 20829 | if (expansionMode == MULTIPLICATIVE_MODE) { |
671 | 20813 | return (internalArray.length / numElements) > contractionCriteria; |
672 | } else { |
|
673 | 16 | 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 | 618 | return startIndex; |
688 | } |
|
689 | ||
690 | } |