views:

217

answers:

4

Hi,

I was wondering what the simplest way would be to implement an array who's rank is specified at runtime.

The example I am working on stores a array of boolean values for lattice points, and I want the user to be able to chose how many spatial dimensions the model uses at runtime.

I've looked at the Array.newInstance() method:

dimensionOfSpace = userInputValue;  // this value comes from GUI or whatever
int latticeLength = 5;  // square lattice for simplicity

int[] dimensions = new int[dimensionOfSpace];
for(int i = 0; i < l.length; i++) l[i] = length; 
Object lattice = Array.newInstance(boolean.class, dimensions);

But accessing these values in any sort of way seems to require horribly slow methods such as recursively using Array.get until the returned value is no longer an array, i.e. using isArray().

Am I missing an obvious solution here? I would love to be able to access the values in a way similar to foo[i][j][k].

+3  A: 

Checkout Java Collections. It contains a class called ArrayList that grows in size as needed.

One dimensional

List<Boolean> a = new ArrayList<Boolean>();

Two Dimensional

List<List<Boolean>> b = new List<List<Boolean>>();

Three Dimensional

List<List<List<Boolean>>> c = new List<List<List<Boolean>>>();

And you'd access the item as c.get(i).get(j).get(k) instead of c[i][j][k] as in a 3d array. Or even better, wrap it in your own Class, and use a get() method there. So it becomes:

c.get(i, j, k);

Edit:

To have a multi-dimensional list of depth N, remove the Boolean type indictor and simply create lists as

List level1 = new ArrayList();
List level2 = new ArrayList();
List level3 = new ArrayList();
level1.add(level2);
level2.add(level3);

and so on..

Anurag
Thanks, this looks much more like the sort of thing I'm after, though I'm still not sure how well this generalises: Ideally I would want:List<List..N..<Boolean>..N..> lattice = new List<List..N..<Boolean>..N..>();where the group of lists is defined N deep at runtime.I suspect that this is probably not possible...
Hemmer
I think lists of boolean will probably not be ideally performant for large sets you may want to use bitwise arithmetic on integers to save space. It would require more coding but perform better.
harschware
You'd have to give up the type-safety but it's surely possible dynamically, see the updated answer
Anurag
Thanks again, I'll give that a try.
Hemmer
+1  A: 

I did a quick google search for "java tensor" which came up with DJEP, could that be something which fits your bill?

disown
+1  A: 

Hi

I'm going to use the term 'rank' to mean the 'number-of-dimensions' in your array. So a vector has rank 1, a matrix has rank 2 and so on. You've already accepted an answer that by your own admission is not quite what you want. Here's an alternative to settling for less:

Recall that computer memory is essentially linear and that what a compiler does when it gives you arrays is actually take care of transforming an index expression into a linear address. This is simplest to think about if you assume that all arrays are in contiguous memory, not always true. Suppose that you make a declaration such as ARRAY_OF_TYPE[10][10][10], ie it has 1000 elements. Then the element at position [3][5][4] is (my arrays are indexed from 1 not 0 -- change the sums that follow if you want to) at location baseAddress+354*size_of_element_of_TYPE.

I expect you know where I'm going on this by now ...

At run time your program prompts for a list of integers from the user. Each integer specifies the size of one of the dimensions of the array, the number of integers specifies the rank of the array. Your program does some multiplications and you allocate a vector of the right length. OK, you have to write the indexing and de-indexing functions, but these should be fairly straightforward.

et voila, you have an array whose rank is established at run time.

Regards

Mark

High Performance Mark
Shoot, you posted your answer while I was still typing mine. +1.
MAK
Well, I didn't spend any time writing code.
High Performance Mark
Sorry I'm new to this accepting solution business (and StackOverflow), though I know its obvious how it works. Your answer looks like a clever way of doing it. I'll try all the suggestions properly in the morning and let you know how I get on/which works best for my purpose. Ideally, I'd like to be running some substantial sized models with this, so I will need to see which ones scale best too. Thanks, Hemmer
Hemmer
+3  A: 

Looks like what you are looking for is for some way to declare how many dimensions an array has at runtime. I don't know how this could be done using a multidimensional ArrayList, or any multidimensional structure where you have to specify the dimensionality at compile time.

The only answer I see is to use a simple linear array wrapped in a class that converts multidimensional coordinate to and from the its position in the underlying array. This is basically how languages such as C stores multidimensional arrays by using one contiguous chunk of memory.

The code would look something like this:

import java.util.*;

class MultiArray<T>{
    private int[] dimensions;
    private Object[] array;

    public MultiArray(int ... dimensions){
        this.dimensions=dimensions;
        //Utils.product returns the product of the ints in an array
        array=new Object[Utils.product(dimensions)];
    }

    public void set(T value, int ... coords){
        int pos=computePos(coords); 
        array[pos]=value;
    }

    public T get(int ... coords){
        int pos=computePos(coords);
        return (T)(array[pos]);
    }

    private int computePos(int[] coords){
        int pos=0;
        int factor=1;
        for (int i=0;i<coords.length;i++){
            pos+=factor*coords[i];
            factor*=dimensions[i];
        }
        return pos;
    }
}

class Main{
    public static void main(String args[]){
        MultiArray<Integer> m=new MultiArray<Integer>(new int[]{5,4,3}); 
        Random r=new Random();

        for(int i=0;i<5;i++)
            for(int j=0;j<4;j++)
                for(int k=0;k<3;k++)
                    m.set(r.nextInt(),i,j,k);
        for(int i=0;i<5;i++){
            for(int j=0;j<4;j++){
                for(int k=0;k<3;k++)
                    System.out.print(m.get(i,j,k)+" ");     
                System.out.println("");
            }
            System.out.println("\n");
        }
    }
}

class Utils{
    public static int product(int...a){
        int ret=1;
        for (int x:a) ret*=x;
        return ret;
    } 
}
MAK
You scratch my back, I scratch yours. And to be more professional about it I think this is a better approach than the one OP has accepted.
High Performance Mark
@High-Performance Mark: Thanks :).
MAK
Hi just an update, I've just managed to sucessfully implement a version of this so thanks to you, and also to High-Performance Mark who had the same idea essentially. The benefits of this method for anyone interested include picking a random point in the lattice/array with just 1 random() statement, rather than N (number of dimensions). Also initialising or doing any bulk sequential stuff can be done with 1 for-loop rather than N loops.Thanks again, Hemmer
Hemmer