views:

224

answers:

6

I am trying to fill a RealVector (from Apache Commons Math) with values. I tried using the class's append method, but that didn't actually add anything. So now I'm using a double[], which works fine, except I don't know in advance how big the array needs to be.

private void runAnalysis() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
 Double attr;
 double[] data = new double[100]; // TODO: bad.

 int i = 0;
 for (Method m : ParseTree.class.getMethods()) {
  if (m.isAnnotationPresent(Analyze.class)) {
   attr = (Double) m.invoke(this);
   analysis.put(m.getAnnotation(Analyze.class).name(), attr);
   data[i++] = attr * m.getAnnotation(Analyze.class).weight();
  }
 }

 weightedAnalysis = new ArrayRealVector(data);
}

How can I deal with this issue? Here are my ideas:

  1. Iterate through the class and count the methods with the annotation, then use that size to initialize the array. However, this will require an extra loop, and reflection is performance-intensive. (right?)

  2. Pick an arbitrary size for the array, doubling it if space runs out. Downside: requires more lines of code

  3. Use a List<Double>, then somehow weasel the Double objects back into doubles so they can be put in the RealVector. Uses more memory for the list.

  4. Just pick a huge size for the starting array, and hope that it never overflows. Downside: this is begging for arrayindexoutofbound errors.

  5. Or am I just using append(double d) wrong?

    private void runAnalysis() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Double attr; weightedAnalysis = new ArrayRealVector(data);

    for (Method m : ParseTree.class.getMethods()) {
     if (m.isAnnotationPresent(Analyze.class)) {
      attr = (Double) m.invoke(this);
      analysis.put(m.getAnnotation(Analyze.class).name(), attr);
      weightedAnalysis.append(attr * m.getAnnotation(Analyze.class).weight());
     }
    }
    

    }

+4  A: 

RealVector.append() doesn't modify the vector, but rather constructs a new vector: The Java doc of RealVector.append() explains:

append

RealVector append(double d)
Construct a vector by appending a double to this vector.
Parameters:
d - double to append.
Returns:
a new vector

Please note that using RealVector to construct the vector is quite an expensive operation, as append() would need to copy the elements over and over (i.e. constructing the array in the way you explained runs in O(n^2) time.

I would recommend simply using java's ArrayList<Double> during construction, and then simply converting to RealVector or any other data abstraction you like.

notnoop
+1  A: 

Why not use an ArrayList and add the elements to that?

Gazler
Why not reading the question message instead of only the question subject?
BalusC
He mentioned that using a List would eat memory. Not a solution.
Jacob Relkin
A: 

You mentioned that you tried the append method, but that didn't actually add anything. After looking at the javadoc, make sure that you assign the result of the append method back to the original value...You probably already tried this, but just in case you overlooked:

RealVector myRealVector = new ArrayRealVector(data);
myRealVector = myRealVector.append(1.0);

in other words, this won't change myRealVector:

RealVector myRealVector = new ArrayRealVector(data);
myRealVector.append(1.0);
Dave Paroulek
+1  A: 

I would suggest 3 as a good option. Using Double vs double is a minimal problem since autoboxing was introduced.

Vincent Ramdhanie
+1  A: 

Using RealVector will take a huge amount of memory and computation time to build, because what you want is:

RealVector newVector = oldVector.append(d);

append() returns a newly constructed object, which is what you'd want for correctness.

If you're okay with heavy overhead on build, take a look at Apache Commons ArrayUtils, specifically add(double) and/or toPrimitive(Double).

Dean J
A: 

you could initialize the array using the

ParseTree.class.getMethods().lenght

as initial capacity:

double[] buf = new double[ ParseTree.class.getMethods().lenght ];

or better

DoubleBuffer buf = DoubleBuffer.allocate([ ParseTree.class.getMethods().lenght);

this may waste some memory but is a safe solution, it depends on how many hit the if inside the loop has.

if you prefer you may count how many methods are annotated in advance and then allocate the exact size for the array

Lorenzo Boccaccia