001 // Copyright 2006-08 Regents of the University of California. May be used
002 // under the terms of the revised BSD license. See LICENSING for details.
003 /**
004 * @author Adrian Mettler
005 */
006 package org.joe_e.array;
007
008 import java.io.IOException;
009 import java.io.ObjectInputStream;
010 import java.io.ObjectOutputStream;
011 import java.util.Arrays;
012 import java.lang.reflect.Array;
013
014 /**
015 * An immutable array of <code>double</code>.
016 */
017 public final class DoubleArray extends PowerlessArray<Double> {
018 static private final long serialVersionUID = 1L;
019
020 private /* final */ transient double[] doubles;
021
022 DoubleArray(double... doubles) {
023 // Use back door constructor that sets backing store to null.
024 // This lets ConstArray's methods know not to use the backing
025 // store for accessing this object.
026 super(null);
027 this.doubles = doubles;
028 }
029
030 /**
031 * Constructs an array of <code>double</code>s.
032 * @param doubles each element
033 */
034 static public DoubleArray array(final double... doubles) {
035 return new DoubleArray(doubles.clone());
036 }
037
038 /*
039 * The following are necessary because otherwise calls with <=4 arguments
040 * are resolved to the superclass PowerlessArray
041 */
042
043 /**
044 * Construct an empty <code>DoubleArray</code>
045 */
046 @SuppressWarnings("unchecked") // the warning here seems completely bogus
047 static public DoubleArray array() {
048 return new DoubleArray(new double[]{});
049 }
050
051 /**
052 * Construct a <code>DoubleArray</code> with one element.
053 * @param value the value
054 */
055 static public DoubleArray array(double value) {
056 return new DoubleArray(new double[]{value});
057 }
058
059 /**
060 * Construct a <code>DoubleArray</code> with two elements.
061 * @param value1 the first value
062 * @param value2 the second value
063 */
064 static public DoubleArray array(double value1, double value2) {
065 return new DoubleArray(new double[]{value1, value2});
066 }
067
068 /**
069 * Construct a <code>DoubleArray</code> with three elements.
070 * @param value1 the first value
071 * @param value2 the second value
072 * @param value3 the third value
073 */
074 static public DoubleArray array(double value1, double value2, double value3) {
075 return new DoubleArray(new double[]{value1, value2, value3});
076 }
077
078 /**
079 * Construct a <code>DoubleArray</code> with four elements.
080 * @param value1 the first value
081 * @param value2 the second value
082 * @param value3 the third value
083 * @param value4 the fourth value
084 */
085 static public DoubleArray array(double value1, double value2, double value3,
086 double value4) {
087 return new DoubleArray(new double[]{value1, value2, value3, value4});
088 }
089
090 // java.io.Serializable interface
091
092 /*
093 * Serialization hacks to prevent the contents from being serialized as a
094 * mutable array. This improves efficiency for projects that serialize
095 * Joe-E objects using Java's serialization API by avoiding treatment of
096 * immutable state as mutable. These methods can otherwise be ignored.
097 */
098 private void writeObject(final ObjectOutputStream out) throws IOException {
099 out.defaultWriteObject();
100
101 out.writeInt(doubles.length);
102 for (double c : doubles) {
103 out.writeDouble(c);
104 }
105 }
106
107 private void readObject(final ObjectInputStream in) throws
108 IOException, ClassNotFoundException {
109 in.defaultReadObject();
110
111 final int length = in.readInt();
112 doubles = new double[length];
113 for (int i = 0; i < length; ++i) {
114 doubles[i] = in.readDouble();
115 }
116 }
117
118 /*
119 * Methods that must be overriden, as the implementation in ConstArray
120 * would try to use arr, which is null.
121 */
122
123 // java.lang.Object interface
124
125 /**
126 * Test for equality with another object
127 * @return true if the other object is a {@link ConstArray} with the same
128 * contents as this array
129 */
130 public boolean equals(final Object other) {
131 if (other instanceof DoubleArray) {
132 // Simple case: just compare doubleArr fields
133 return Arrays.equals(doubles, ((DoubleArray)other).doubles);
134 } else if (other instanceof ConstArray<?>) {
135 // Other array does not have contents in doubleArr:
136 // check that length matches, and then compare elements one-by-one
137 final ConstArray<?> otherArray = (ConstArray<?>)other;
138 if (otherArray.length() != doubles.length) {
139 return false;
140 }
141 for (int i = 0; i < doubles.length; ++i) {
142 final Object otherElement = otherArray.get(i);
143 if (!(otherElement instanceof Double) ||
144 ((Double)otherElement).doubleValue() != doubles[i]) {
145 return false;
146 }
147 }
148 return true;
149 } else {
150 // Only a ConstArray can be equal to a DoubleArray
151 return false;
152 }
153 }
154
155 /**
156 * Computes a digest of the array for hashing. The hash code is the same
157 * as {@link java.util.Arrays#hashCode(Object[])} called on a Java array
158 * containing the same elements.
159 * @return a hash code based on the contents of this array
160 */
161 public int hashCode() {
162 // Because wrappers for primitive types return the same hashCode as
163 // their primitive values, a DoubleArray has the same hashCode as a
164 // ConstArray<Double> with the same contents.
165 return Arrays.hashCode(doubles);
166 }
167
168 /**
169 * Return a string representation of the array
170 */
171 public String toString() {
172 return Arrays.toString(doubles);
173 }
174
175 // org.joe_e.ConstArray interface
176
177 /**
178 * Gets the length of the array.
179 */
180 public int length() {
181 return doubles.length;
182 }
183
184 /**
185 * Creates a <code>Double</code> for a specified <code>double</code>.
186 * @param i position of the element to return
187 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds
188 */
189 public Double get(int i) {
190 return doubles[i];
191 }
192
193 /**
194 * Return a mutable copy of the array
195 * @param prototype prototype of the array to copy into
196 * @return an array containing the contents of this <code>ConstArray</code>
197 * of the same type as <code>prototype</code>
198 * @throws ArrayStoreException if an element cannot be stored in the array
199 */
200 @SuppressWarnings("unchecked")
201 public <T> T[] toArray(T[] prototype) {
202 final int len = length();
203 if (prototype.length < len) {
204 final Class<?> t = prototype.getClass().getComponentType();
205 prototype = (T[])Array.newInstance(t, len);
206 }
207
208 for (int i = 0; i < len; ++i) {
209 prototype[i] = (T) (Double) doubles[i];
210 }
211 return prototype;
212 }
213
214 /**
215 * Creates a <code>DoubleArray<code> with an appended <code>Double</code>.
216 * @param newDouble the element to append
217 * @throws NullPointerException <code>newDouble</code> is null
218 */
219 public DoubleArray with(final Double newDouble) {
220 return with(newDouble.doubleValue());
221 }
222
223 /*
224 * Convenience (more efficient) methods with double
225 */
226
227 /**
228 * Gets the <code>double</code> at a specified position.
229 * @param i position of the element to return
230 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds
231 */
232 public double getDouble(final int i) {
233 return doubles[i];
234 }
235
236 /**
237 * Creates a mutable copy of the <code>double</code> array
238 */
239 public double[] toDoubleArray() {
240 return doubles.clone();
241 }
242
243 /**
244 * Creates a <code>DoubleArray</code> with an appended <code>double</code>.
245 * @param newDouble the element to append
246 */
247 public DoubleArray with(final double newDouble) {
248 final double[] newDoubles = new double[doubles.length + 1];
249 System.arraycopy(doubles, 0, newDoubles, 0, doubles.length);
250 newDoubles[doubles.length] = newDouble;
251 return new DoubleArray(newDoubles);
252 }
253
254 /**
255 * Return a new <code>DoubleArray</code> that contains the same elements
256 * as this one excluding the element at a specified index
257 * @param i the index of the element to exclude
258 * @return the new array
259 */
260 public DoubleArray without(final int i) {
261 final double[] newArr = new double[doubles.length - 1];
262 System.arraycopy(doubles, 0, newArr, 0, i);
263 System.arraycopy(doubles, i + 1, newArr, i, newArr.length - i);
264 return new DoubleArray(newArr);
265 }
266
267 /**
268 * A {@link DoubleArray} factory.
269 */
270 public static final class Builder extends
271 PowerlessArray.Builder<Double> {
272 private double[] doubleBuffer;
273
274 /**
275 * Construct an instance with the default internal array length.
276 */
277 Builder() {
278 this(0);
279 }
280
281 /**
282 * Construct an instance.
283 * @param estimate estimated array length
284 */
285 Builder(int estimate) {
286 doubleBuffer = new double[estimate > 0 ? estimate : 32];
287 size = 0;
288 }
289
290 // ArrayBuilder<Double> interface
291 /**
292 * Append a <code>Double</code>
293 * @param newDouble the element to add
294 * @throws NegativeArraySizeException if the resulting internal array
295 * would exceed the maximum length of a Java array. The builder is
296 * unmodified.
297 */
298 public void append(Double newDouble) {
299 append ((double) newDouble);
300 }
301
302 /**
303 * Append an array of <code>Double</code>s
304 * @param newDoubles the elements to add
305 * @throws IndexOutOfBoundsException if the resulting internal array
306 * would exceed the maximum length of a Java array. The builder is
307 * unmodified.
308 */
309 public void append(final Double[] newDoubles) {
310 append(newDoubles, 0, newDoubles.length);
311 }
312
313 /**
314 * Append a range of elements from an array of <code>Double</code>s
315 * @param newDoubles the array to add elements from
316 * @param off the index of the first element to add
317 * @param len the number of elements to add
318 * @throws IndexOutOfBoundsException if an out-of-bounds index would
319 * be referenced or the resulting internal array would exceed the
320 * maximum length of a Java array. The builder is unmodified.
321 */
322 public void append(final Double[] newDoubles,
323 final int off, final int len) {
324 int newSize = size + len;
325 if (newSize < 0 || off < 0 || len < 0 || off + len < 0
326 || off + len > newDoubles.length) {
327 throw new IndexOutOfBoundsException();
328 }
329 if (newSize > doubleBuffer.length) {
330 int newLength = Math.max(newSize, 2 * doubleBuffer.length);
331 System.arraycopy(doubleBuffer, 0,
332 doubleBuffer = new double[newLength], 0, size);
333 }
334
335 for (int i = 0; i < len; ++i) {
336 doubleBuffer[size + i] = newDoubles[off + i];
337 }
338 size = newSize;
339 }
340
341 /**
342 * Create a snapshot of the current content.
343 * @return a <code>DoubleArray</code> containing the elements so far
344 */
345 public DoubleArray snapshot() {
346 final double[] arr;
347 if (size == doubleBuffer.length) {
348 arr = doubleBuffer;
349 } else {
350 arr = new double[size];
351 System.arraycopy(doubleBuffer, 0, arr, 0, size);
352 }
353 return new DoubleArray(arr);
354 }
355
356 /*
357 * Convenience (more efficient) methods with double
358 */
359 /**
360 * Append a <code>double</code>
361 * @param newDouble the element to add
362 * @throws NegativeArraySizeException if the resulting internal array
363 * would exceed the maximum length of a Java array. The builder is
364 * unmodified.
365 */
366 public void append(final double newDouble) {
367 if (size == doubleBuffer.length) {
368 System.arraycopy(doubleBuffer, 0,
369 doubleBuffer = new double[2 * size], 0, size);
370 }
371 doubleBuffer[size++] = newDouble;
372 }
373
374 /**
375 * Append an array of <code>double</code>s
376 * @param newDoubles the elements to add
377 * @throws IndexOutOfBoundsException if the resulting internal array
378 * would exceed the maximum length of a Java array. The builder is
379 * unmodified.
380 */
381 public void append(final double[] newDoubles) {
382 append(newDoubles, 0, newDoubles.length);
383 }
384
385 /**
386 * Append a range of elements from an array of <code>double</code>s
387 * @param newDoubles the array to add elements from
388 * @param off the index of the first element to add
389 * @param len the number of elements to add
390 * @throws IndexOutOfBoundsException if an out-of-bounds index would
391 * be referenced or the resulting internal array would exceed the
392 * maximum length of a Java array. The builder is unmodified.
393 */
394 public void append(final double[] newDoubles, final int off,
395 final int len) {
396 int newSize = size + len;
397 if (newSize < 0 || off < 0 || len < 0 || off + len < 0
398 || off + len > newDoubles.length) {
399 throw new IndexOutOfBoundsException();
400 }
401 if (newSize > doubleBuffer.length) {
402 int newLength = Math.max(newSize, 2 * doubleBuffer.length);
403 System.arraycopy(doubleBuffer, 0,
404 doubleBuffer = new double[newLength], 0, size);
405 }
406 System.arraycopy(newDoubles, off, doubleBuffer, size, len);
407 size = newSize;
408 }
409 }
410
411 /* If one only invokes static methods statically, this is sound, since
412 * ByteArray extends PowerlessArray<Byte> and thus this method is
413 * only required to return something of a type covariant with
414 * PowerlessArray.Builder<Byte>. Unfortunately, this is not completely
415 * sound because it is possible to invoke static methods on instances, e.g.
416 * ConstArray.Builder<String> = (ConstArray (DoubleArray.array())).builder()
417 * Invocations of append() can then throw ClassCastExceptions.
418 *
419 * I can't see a way to avoid this other than to de-genericize everything.
420 */
421
422 /**
423 * Get a <code>DoubleArray.Builder</code>. This is equivalent to the
424 * constructor.
425 * @return a new builder instance, with the default internal array length
426 */
427 @SuppressWarnings("unchecked")
428 public static Builder builder() {
429 return new Builder(0);
430 }
431
432 /**
433 * Get a <code>DoubleArray.Builder</code>. This is equivalent to the
434 * constructor.
435 * @param estimate estimated array length
436 * @return a new builder instance
437 */
438 @SuppressWarnings("unchecked")
439 public static Builder builder(final int estimate) {
440 return new Builder(estimate);
441 }
442 }