2013-10-14 27 views
3

Предположим, что мне задан класс объекта и int[], описывающий произвольные длины многомерного массива, который я хочу создать (и работать с). До сих пор я решил, что могу использовать java.lang.reflect.Array.newInstance(Class<?> type, int... sizes), но в этот момент я застрял. Другими словами,Работа с многомерными динамическими массивами в Java

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); 

После определения Object o я не знаю, как поступить. Априори, я не знаю, будет ли typefloat.class или каким-либо другим типом (на данный момент я предполагаю, что это базовый тип). Хуже того, int[] sizes может быть любым.

Чтобы сделать это более конкретной проблемой, как я должен, скажем, установить каждый элемент многомерного массива o с заданным значением, скажет 5.6 после того как я инстанцировал его с Array.newInstance?

+0

Может быть, пришло время попробовать что-то динамичное? например ruby, python или javascript :) – Admit

+0

Рад. За исключением того, что это работа с существующим Java API (netCDF) и выполнение большого количества if-elses для учета разных случаев (до 7-мерных массивов чего-либо от шорт до ints до плавающих до двойных строк) может но это будет «грязное» решение. Может быть, это не выглядит аккуратно, но, по крайней мере, это сработает? – jbatista

+0

@dbw В приведенном выше примере у меня был бы способ получить класс объектов в каждом слоте многомерного массива (float.class - это один пример) и массив с длиной каждый из размеров массива. Поэтому в этом случае 'Object o' действительно является' float [10] [400] [300] '- но это может быть что-то еще, например. 'type = String.class; size = new int [] {0} 'будет одной строкой. Надеюсь, это более ясно. – jbatista

ответ

0

Я в конечном итоге кодирование решения вместо того, чтобы пытаться найти/использовать методы работы с массивом из API Java (только один ниже - java.util.Arrays.copyOf(Object[], int).

Итак, ниже описаны методы, которые я написал. Я не тестировал их широко (я не удивлюсь, если там есть неэффективность, я просто попробовал первое, что оказалось работать) поэтому я ожидаю, что следующее решение можно адаптировать в соответствии с потребностями других:

/** 
* 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; 
} 
0

В классе java.lang.reflect.Array есть другие полезные методы, такие как get(Object array, int index) и setFloat(Object array, int index, float f). Используя их комбинации, нужно иметь возможность заполнить весь многомерный массив.

Грубый псевдокод:

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) 
       } 
      } 
     ... 
    } 
} 

Если я правильно читал API, что-то, как это должно работать.

+0

Прочтите мой комментарий. Я пробовал это, и он не работает, потому что метод get предполагает, что в качестве входных данных используются одномерные массивы. – Thorn

+0

+1, потому что это указывало на «Array.set», который я смог использовать в своем решении. –

+0

@Thorn: Что? Ты уверен? 'java.lang.reflect.Array.get (новый int [4] [4], 0);' похоже, работает на моем конце. – Smallhacker

1

Хороший вопрос. Это действительно сложно, потому что, похоже, не существует способа бросить объект в массив заданных измерений. Я не мог найти способ получить правильный многомерный объект массива из созданного экземпляра. Но если вы просто хотите получить и установить элементы в созданном массиве, вы можете использовать следующий метод:

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); 
    } 
} 

set метод принимает индекс, чтобы установить, как массив. Так set(f, new int[] {0,0,1}, 3.0f); должен в основном делать f[0][0][1] = 3.0f

EDIT: Добавлен немного уродливое исправление для обработки одномерных массивов

Смежные вопросы