diff --git a/src/java/org/apache/commons/math/util/ResizableDoubleArray.java b/src/java/org/apache/commons/math/util/ResizableDoubleArray.java index dab48955f..22f3e5558 100644 --- a/src/java/org/apache/commons/math/util/ResizableDoubleArray.java +++ b/src/java/org/apache/commons/math/util/ResizableDoubleArray.java @@ -17,6 +17,7 @@ package org.apache.commons.math.util; import java.io.Serializable; +import java.util.Arrays; import org.apache.commons.math.MathRuntimeException; @@ -243,6 +244,18 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { setExpansionMode(expansionMode); internalArray = new double[initialCapacity]; } + + /** + * Copy constructor. Creates a new ResizableDoubleArray that is a deep, + * fresh copy of the original. Needs to acquire synchronization lock + * on original. Original may not be null; otherwise a NullPointerException + * is thrown. + * + * @param original + */ + public ResizableDoubleArray(ResizableDoubleArray original) { + copy(original, this); + } /** * Adds an element to the end of this expandable array. @@ -294,27 +307,28 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } return discarded; } - + public synchronized double substituteMostRecentElement(double value) { if (numElements < 1) { - throw MathRuntimeException.createArrayIndexOutOfBoundsException("cannot substitute an element from an empty array", - null); + throw MathRuntimeException.createArrayIndexOutOfBoundsException( + "cannot substitute an element from an empty array", null); } double discarded = internalArray[startIndex + (numElements - 1)]; - internalArray[startIndex + (numElements - 1)] = value; + internalArray[startIndex + (numElements - 1)] = value; - return discarded; + return discarded; } + /** * Checks the expansion factor and the contraction criteria and throws an * IllegalArgumentException if the contractionCriteria is less than the * expansionCriteria * * @param expansionFactor factor to be checked - * @param contractionCritera critera to be checked + * @param contractionCritera criteria to be checked * @throws IllegalArgumentException if the contractionCriteria is less than * the expansionCriteria. */ @@ -386,8 +400,8 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ public synchronized void discardFrontElements(int i) { - discardExtremeElements(i,true); - + discardExtremeElements(i,true); + } /** @@ -402,10 +416,10 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ public synchronized void discardMostRecentElements(int i) { - discardExtremeElements(i,false); - + discardExtremeElements(i,false); + } - + /** * Discards the i first or last elements of the array, * depending on the value of front. @@ -453,7 +467,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { */ protected synchronized void expand() { - // notice the use of Math.ceil(), this gaurantees that we will always + // notice the use of Math.ceil(), this guarantees that we will always // have an array of at least currentSize + 1. Assume that the // current initial capacity is 1 and the expansion factor // is 1.000000000000000001. The newly calculated size will be @@ -534,7 +548,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } /** - * The expansion factor controls the size of a new aray when an array + * The expansion factor controls the size of a new array when an array * needs to be expanded. The expansionMode * determines whether the size of the array is multiplied by the * expansionFactor (MULTIPLICATIVE_MODE) or if @@ -704,7 +718,7 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { } // Test the new num elements, check to see if the array needs to be - // expanded to accomodate this new number of elements + // expanded to accommodate this new number of elements if ((startIndex + i) > internalArray.length) { expandTo(startIndex + i); } @@ -739,5 +753,94 @@ public class ResizableDoubleArray implements DoubleArray, Serializable { public synchronized int start() { return startIndex; } - + + /** + *

Copies source to dest, copying the underlying data, so dest is + * a new, independent copy of source. Does not contract before + * the copy.

+ * + *

Obtains synchronization locks on both source and dest + * (in that order) before performing the copy.

+ * + *

Neither source nor dest may be null; otherwise a NullPointerException + * is thrown

+ * + */ + public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) { + synchronized(source) { + synchronized(dest) { + dest.initialCapacity = source.initialCapacity; + dest.contractionCriteria = source.contractionCriteria; + dest.expansionFactor = source.expansionFactor; + dest.expansionMode = source.expansionMode; + dest.internalArray = new double[source.internalArray.length]; + System.arraycopy(source.internalArray, 0, dest.internalArray, + 0, dest.internalArray.length); + dest.numElements = source.numElements; + dest.startIndex = source.startIndex; + } + } + } + + /** + * Returns a copy of the ResizableDoubleArray. Does not contract before + * the copy, so the returned object is an exact copy of this. + * + * @return a new ResizableDoubleArray with the same data and configuration + * properties as this + */ + public synchronized ResizableDoubleArray copy() { + ResizableDoubleArray result = new ResizableDoubleArray(); + copy(this, result); + return result; + } + + /** + * Returns true iff object is a ResizableDoubleArray with the same properties + * as this and an identical internal storage array. + * + * @param object object to be compared for equality with this + * @return true iff object is a ResizableDoubleArray with the same data and + * properties as this + * + */ + public boolean equals(Object object) { + if (object == this ) { + return true; + } + if (object instanceof ResizableDoubleArray == false) { + return false; + } + boolean result = true; + ResizableDoubleArray other = (ResizableDoubleArray) object; + result = result && (other.initialCapacity == initialCapacity); + result = result && (other.contractionCriteria == contractionCriteria); + result = result && (other.expansionFactor == expansionFactor); + result = result && (other.expansionMode == expansionMode); + result = result && (other.numElements == numElements); + result = result && (other.startIndex == startIndex); + if (!result) { + return false; + } else { + return Arrays.equals(internalArray, other.internalArray); + } + } + + /** + * Returns a hash code consistent with equals. + * + * @return hash code representing this ResizableDoubleArray + */ + public int hashCode() { + int[] hashData = new int[7]; + hashData[0] = Arrays.hashCode(internalArray); + hashData[1] = new Float(expansionFactor).hashCode(); + hashData[2] = new Float(contractionCriteria).hashCode(); + hashData[3] = initialCapacity; + hashData[4] = expansionMode; + hashData[5] = numElements; + hashData[6] = startIndex; + return Arrays.hashCode(hashData); + } + } diff --git a/src/test/org/apache/commons/math/util/ResizableDoubleArrayTest.java b/src/test/org/apache/commons/math/util/ResizableDoubleArrayTest.java index 265d5d24e..5c18d7ba5 100644 --- a/src/test/org/apache/commons/math/util/ResizableDoubleArrayTest.java +++ b/src/test/org/apache/commons/math/util/ResizableDoubleArrayTest.java @@ -106,6 +106,14 @@ public class ResizableDoubleArrayTest extends DoubleArrayAbstractTest { // expected } + // Copy constructor + testDa = new ResizableDoubleArray(2, 2.0f, 3.0f, + ResizableDoubleArray.ADDITIVE_MODE); + testDa.addElement(2.0); + testDa.addElement(3.2); + ResizableDoubleArray copyDa = new ResizableDoubleArray(testDa); + assertEquals(copyDa, testDa); + assertEquals(testDa, copyDa); } @@ -402,4 +410,93 @@ public class ResizableDoubleArrayTest extends DoubleArrayAbstractTest { // expected } } + + public void testEqualsAndHashCode() throws Exception { + + // Wrong type + ResizableDoubleArray first = new ResizableDoubleArray(); + Double other = new Double(2); + assertFalse(first.equals(other)); + + // Null + other = null; + assertFalse(first.equals(other)); + + // Reflexive + assertTrue(first.equals(first)); + + // Argumentless constructor + ResizableDoubleArray second = new ResizableDoubleArray(); + verifyEquality(first, second); + + // Equals iff same data, same properties + ResizableDoubleArray third = new ResizableDoubleArray(3, 2.0f, 2.0f); + verifyInequality(third, first); + ResizableDoubleArray fourth = new ResizableDoubleArray(3, 2.0f, 2.0f); + ResizableDoubleArray fifth = new ResizableDoubleArray(2, 2.0f, 2.0f); + verifyEquality(third, fourth); + verifyInequality(third, fifth); + third.addElement(4.1); + third.addElement(4.2); + third.addElement(4.3); + fourth.addElement(4.1); + fourth.addElement(4.2); + fourth.addElement(4.3); + verifyEquality(third, fourth); + + // expand + fourth.addElement(4.4); + verifyInequality(third, fourth); + third.addElement(4.4); + verifyEquality(third, fourth); + fourth.addElement(4.4); + verifyInequality(third, fourth); + third.addElement(4.4); + verifyEquality(third, fourth); + fourth.addElementRolling(4.5); + third.addElementRolling(4.5); + verifyEquality(third, fourth); + + // discard + third.discardFrontElements(1); + verifyInequality(third, fourth); + fourth.discardFrontElements(1); + verifyEquality(third, fourth); + + // discard recent + third.discardMostRecentElements(2); + fourth.discardMostRecentElements(2); + verifyEquality(third, fourth); + + // wrong order + third.addElement(18); + fourth.addElement(17); + third.addElement(17); + fourth.addElement(18); + verifyInequality(third, fourth); + + // copy + ResizableDoubleArray.copy(fourth, fifth); + verifyEquality(fourth, fifth); + + // Copy constructor + verifyEquality(fourth, new ResizableDoubleArray(fourth)); + + // Instance copy + verifyEquality(fourth, fourth.copy()); + + } + + private void verifyEquality(ResizableDoubleArray a, ResizableDoubleArray b) { + assertTrue(b.equals(a)); + assertTrue(a.equals(b)); + assertEquals(a.hashCode(), b.hashCode()); + } + + private void verifyInequality(ResizableDoubleArray a, ResizableDoubleArray b) { + assertFalse(b.equals(a)); + assertFalse(a.equals(b)); + assertFalse(a.hashCode() == b.hashCode()); + } + }