3

Suppose that I'm given an object class and an int[] describing the arbitrary lengths of an multi-dimensional array that I wish to instantiate (and work with). So far, I've worked out that I can use java.lang.reflect.Array.newInstance(Class<?> type, int... sizes), but at that point I'm stuck. In other words,

Class<?> type = float.class;  // this varies (I receive this)
int[] sizes = new int[]{ 10, 400, 300 }; // this varies too, it could be e.g. int[]{0} for a scalar
Object o = (Object) java.lang.reflect.Array.newInstance(type, sizes);

After defining Object o I don't know how to proceed. A priori, I don't know if type is going to be float.class or some other type (for now I assume it's a basic type). Worse than that, int[] sizes can be anything.

To make it a more concrete problem, how am I supposed to, say, set each element of the multi-dimensional array o with a given value, say 5.6 after I've instantiated it with Array.newInstance?

8
  • May be it's time to try something dynamic? e.g. ruby, python or javascript :) Commented Oct 14, 2013 at 10:22
  • Gladly. Except this is to work with an existing Java API (netCDF), and doing a whole lot of if-elses to account for the different cases (up to 7-dimensional arrays of anything from shorts to ints to floats to doubles to Strings) could work but it'd be a "dirty" solution. Maybe it doesn't look neat, but at least it'd work? Commented Oct 14, 2013 at 10:26
  • @dbw In the example I've provided, I'd have a way to get the class of objects in each slot of the multi-dimensional array (float.class is one example), and an array with the length of each of the array's dimensions. So in this case, Object o is really a float[10][400][300] -- but it could be something else e.g. type = String.class; sizes = new int[]{0} would be a single String. I hope that's more clear. Commented Oct 14, 2013 at 10:41
  • 1
    This is a brain bender! I almost have a recursive solution, but I hit a wall with Object row = java.lang.reflect.Array.get(multiArray, i); because get only works with one dimensional arrays. Do we have an upper bound on the number of dimensions? (Most platforms have a limit of 255, but maybe your application has a smaller limit) Commented Oct 14, 2013 at 11:08
  • 2
    I think I'd give up on [] arrays and switch to one of the List classes, with Integer, Float, etc wrappers for the primitives. Of course, the generic stuff will make this harder, but it can probably be worked out. Commented Oct 14, 2013 at 11:37

3 Answers 3

1

Good question. This is really tricky because there doesn't seem to be a way to cast an object to an array of given dimensions. I couldn't find a way to get a proper multidimensional array object out of the created instance. But if you just want to get and set elements in the created array, you can use the following method:

public static void main(String[] args) {
    Class<?> type = float.class; // this varies (I receive this)
    int[] sizes = new int[] { 1, 3 }; // this varies too, it could be e.g.
                                    // int[]{0} for a scalar
    Object f = Array.newInstance(type, sizes);
    set(f, new int[] { 0, 2 }, 3.0f);
    if (f instanceof Object[])
        System.out.println(Arrays.deepToString((Object[]) f));
    else {
        int l = Array.getLength(f);
        for (int i = 0; i < l; ++i) {
            System.out.print(Array.get(f, i) + ", ");
        }
        System.out.println();
    }
}

public static void set(Object arr, int[] indexPath, Object value) {
    if (arr instanceof Object[]) {
        Object[] temp= (Object[]) arr;
        for (int i = 0; i < indexPath.length - 2; ++i) {
            temp = (Object[]) temp[indexPath[i]];
        }
        Array.set(temp[indexPath[indexPath.length - 2]],
            indexPath[indexPath.length - 1], value);
    } else {
        Array.set(arr,
                indexPath[0], value);
    }
}

The set method takes the index to set as an array. So set(f, new int[] {0,0,1}, 3.0f); should basically do f[0][0][1] = 3.0f

EDIT: Added a slightly ugly fix to handle single dimensional arrays

Sign up to request clarification or add additional context in comments.

Comments

0

Looking in the java.lang.reflect.Array class, there are other useful methods, such as get(Object array, int index) and setFloat(Object array, int index, float f). Using combinations of these, one should be able to fill the entire multi-dimensional array.

Rough pseudocode:

Object level0 = createArray(...)
For i1 = 0 to (length of dimension 1) {
    Object level1 = get(level0, i1)
    For i2 = 0 to (length of dimension 2) {
        Object level2 = get(level1, i2)
        ...etc...
            For iM = 0 to (length of dimension M) {
                Object levelM = get(levelL, iM)
                For iN = 0 to (length of dimension N) {
                    setFloat(levelM, iN, 5.6f)
                }
            }
        ...
    }
}

If I'm reading the API correctly, something like this should work.

3 Comments

Read my comment. I tried that and it doesn't work because the get method expects single dimensional arrays as input.
+1 because this pointed me to Array.set which I was able to use in my solution.
@Thorn: What? Are you sure? java.lang.reflect.Array.get(new int[4][4], 0); seems to works on my end.
0

I ended up coding the solution instead of attempting to find/use array manipulation methods from the Java API (the only one below is java.util.Arrays.copyOf(Object[], int).

So, below are the methods I wrote. I did not test them extensively (I wouldn't be surprised if there are inefficiencies there, I just tried the first thing that appeared to work), so I expect that the following solution can be adapted to suit others' needs:

/**
 * Creates an instance of Object multi-dimensional arrays, with dimensions specified by the argument.
 * 
 * @example Create an array Object[4][20][30]:  
 * <pre>
 * Object[] array = newArray(new int[]{4,20,30});
 * </pre>
 * 
 * @param sizes The list of dimension lengths.
 * @return 
 */
public static Object[] newArray(int[] sizes) {
    Object[] result = null;
    if (sizes != null) {
        for(int n = sizes.length - 1; n >= 0; n--) {
            if (result == null) {
                result = new Object[sizes[n]];
            } else {
                Object[] oa = new Object[sizes[n]];
                for (int i = 0; i < sizes[n]; i++) {
                    oa[i] = java.util.Arrays.copyOf(result, result.length);
                }
                result = oa;
            }
        }
        if (result == null) { result = new Object[1]; }
    }
    return result;
}

/**
 * Get the value of a multi-dimensional array element given by a coordinate list.
 * 
 * @example Read the value at [2][14][27]:
 * <pre>
 * Object[] array;  // e.g. a int[4][20][30] created with newArray(int[])
 * int[] coord = new int[]{2,14,27};
 * Object value = getValue(array, coord);
 * </pre>
 * 
 * @param array The coordinates of the array element.
 * @param coordinates
 * @return
 */
public static Object getValue(Object[] array, int[] coordinates) {
    Object result = null;
    if (array == null || coordinates == null || 0 > coordinates[0]||coordinates[0] > array.length) {
        result = null;
    } else {
        int x = coordinates[0];
        if (array[x] instanceof Object[]) {
            int[] c = new int[coordinates.length-1];
            for(int i = 0; i < c.length; i++) { c[i] = coordinates[i + 1]; }
            result = getValue((Object[]) array[x], c);
        } else {
            result = array[x];
        }
    }
    return result;
}

/**
 * Set the value of a multi-dimensional array element given a list designating the element's coordinates.
 * 
 * @example Write a value to [1][0][7]:
 * <pre>
 * Object value;    // e.g. a float
 * Object[] array;  // e.g. a int[4][20][30] created with newArray(int[])
 * int[] coord = new int[]{1,0,7,};
 * setValue(array, coord, value);
 * </pre>
 * 
 * @param array
 * @param coordinates
 * @param value
 * @return
 */
public static void setValue(Object[] array, int[] coordinates, Object value) {
    if (array == null || coordinates == null || array.length == 0 || coordinates.length == 0 || array.length < coordinates[0]||coordinates[0] < 0 ) {
        return;
    } else {
        int x = coordinates[0];
        if (array[x] != null && array[x].getClass().isArray()) {    // recurse
            int[] c = new int[coordinates.length - 1];
            for (int i = 0; i < c.length; i++) { c[i] = coordinates[i + 1]; }
            setValue((Object[]) array[x], c, value); 
        } else {
            array[x] = value;
        }
    }
    return;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.