是否可以在Java中动态构建多维数组?

时间:2010-06-23 18:40:55

标签: java arrays dynamic multidimensional-array

假设我们有Java代码:

Object arr = Array.newInstance(Array.class, 5);

那会跑吗?作为进一步说明,如果我们尝试这样的事情会怎么样:

Object arr1 = Array.newInstance(Array.class, 2);
Object arr2 = Array.newInstance(String.class, 4);
Object arr3 = Array.newInstance(String.class, 4);
Array.set(arr1, 0, arr2);
Array.set(arr1, 1, arr3);

然后,arr1将成为相当于:

的二维数组
String[2][4] arr1;

这个怎么样:如果我们在运行时之前不知道这个数组的维度呢?

编辑:如果这有帮助(我确信它会......)我们试图从表格的字符串中解析未知维度的数组

[value1, value2, ...]

[ [value11, value12, ...] [value21, value22, ...] ...]

等等

Edit2:如果有人像我一样愚蠢尝试这个垃圾,这里是一个至少编译和运行的版本。逻辑是否合理完全是另一个问题......

Object arr1 = Array.newInstance(Object.class, x);
Object arr11 = Array.newInstance(Object.class, y);
Object arr12 = Array.newInstance(Object.class, y);
...
Object arr1x = Array.newInstance(Object.class, y);
Array.set(arr1, 0, arr11);
Array.set(arr1, 1, arr12);
...
Array.set(arr1, x-1, arr1x);

等等。它只需要是一个巨大的嵌套对象数组

7 个答案:

答案 0 :(得分:19)

实际上可以在java中完成。 (我必须说,我有点惊讶。)

声明;我从来没有想过在其他地方看到这个代码而不是这个问题的答案。我强烈建议您使用List s。

import java.lang.reflect.Array;
import java.util.*;

public class Test {

    public static int[] tail(int[] arr) {
        return Arrays.copyOfRange(arr, 1, arr.length);
    }

    public static void setValue(Object array, String value, int... indecies) {
        if (indecies.length == 1)
            ((String[]) array)[indecies[0]] = value;
        else
            setValue(Array.get(array, indecies[0]), value, tail(indecies));
    }

    public static void fillWithSomeValues(Object array, String v, int... sizes) {
        for (int i = 0; i < sizes[0]; i++)
            if (sizes.length == 1)
                ((String[]) array)[i] = v + i;
            else
                fillWithSomeValues(Array.get(array, i), v + i, tail(sizes));
    }

    public static void main(String[] args) {

        // Randomly choose number of dimensions (1, 2 or 3) at runtime.
        Random r = new Random();
        int dims = 1 + r.nextInt(3);

        // Randomly choose array lengths (1, 2 or 3) at runtime.
        int[] sizes = new int[dims];
        for (int i = 0; i < sizes.length; i++)
            sizes[i] = 1 + r.nextInt(3);

        // Create array
        System.out.println("Creating array with dimensions / sizes: " +
                Arrays.toString(sizes).replaceAll(", ", "]["));
        Object multiDimArray = Array.newInstance(String.class, sizes);

        // Fill with some 
        fillWithSomeValues(multiDimArray, "pos ", sizes);

        System.out.println(Arrays.deepToString((Object[]) multiDimArray));


    }
}

示例输出:

Creating array with dimensions / sizes: [2][3][2]
[[[pos 000, pos 001], [pos 010, pos 011], [pos 020, pos 021]],
 [[pos 100, pos 101], [pos 110, pos 111], [pos 120, pos 121]]]

答案 1 :(得分:4)

数组在java中是类型安全的 - 适用于简单数组和“多维”数组 - 即数组数组。

如果嵌套的深度在运行时是可变的,那么你可以做的最好的事情就是使用一个与已知的最小嵌套深度相对应的数组(大概是1)。这个数组中的元素可以是简单的元素,或者如果需要进一步嵌套,则另一个数组。 Object []数组允许您这样做,因为嵌套数组本身也被视为对象,因此适合类型系统。

如果嵌套是完全规则的,那么你可以使用Array.newInstance(String.class, dimension1, dimension2, ...)抢占这种规律并创建一个合适的多维数组,如果嵌套是不规则的,你最好使用嵌套列表,这样就可以“锯齿状” “结构和动态尺寸。在泛型的基础上,你可以有一个锯齿状的结构。如果结构是锯齿状的,则不能使用泛型,因为某些元素可能是简单的项目,而其他元素可能是进一步嵌套的列表。

答案 2 :(得分:2)

因此,您可以将多个维度传递给Array.newInstance,但这会强制每个维度固定长度。如果没关系,可以使用:

// We already know from scanning the input that we need a 2 x 4 array.
// Obviously this array would be created some other way. Probably through
// a List.toArray operation.
final int[] dimensions = new int[2];
dimensions[0] = 2;
dimensions[1] = 4;

// Create the array, giving the dimensions as the second input.
Object array = Array.newInstance(String.class, dimensions);

// At this point, array is a String[2][4].
// It looks like this, when the first dimension is output:
// [[Ljava.lang.String;@3e25a5, [Ljava.lang.String;@19821f]
//
// The second dimensions look like this:
// [null, null, null, null]

另一种选择是从底部构建它们,使用前一级数组上的getClass作为下一级别的输入。以下代码运行并生成由节点定义的锯齿状数组:

import java.lang.reflect.Array;

public class DynamicArrayTest
{
    private static class Node
    {
        public java.util.List<Node> children = new java.util.LinkedList<Node>();
        public int length = 0;
    }

    public static void main(String[] args)
    {
        Node node1 = new Node();
        node1.length = 1;

        Node node2 = new Node();
        node2.length = 2;

        Node node3 = new Node();
        node3.length = 3;

        Node node4 = new Node();
        node4.children.add(node1);
        node4.children.add(node2);

        Node node5 = new Node();
        node5.children.add(node3);

        Node node6 = new Node();
        node6.children.add(node4);
        node6.children.add(node5);

        Object array = createArray(String.class, node6);
        outputArray(array); System.out.println();
    }

    private static Object createArray(Class<?> type, Node root)
    {
        if (root.length != 0)
        {
            return Array.newInstance(type, root.length);
        }
        else
        {
            java.util.List<Object> children = new java.util.ArrayList<Object>(root.children.size());
            for(Node child : root.children)
            {
                children.add(createArray(type, child));
            }

            Object array = Array.newInstance(children.get(0).getClass(), children.size());
            for(int i = 0; i < Array.getLength(array); ++i)
            {
                Array.set(array, i, children.get(i));
            }

            return array;
        }
    }

    private static void outputArray(Object array)
    {
        System.out.print("[ ");
        for(int i = 0; i < Array.getLength(array); ++i)
        {
            Object element = Array.get(array, i);
            if (element != null && element.getClass().isArray())
                outputArray(element);
            else
                System.out.print(element);

            System.out.print(", ");
        }
        System.out.print("]");
    }
}

答案 3 :(得分:1)

好的,如果您不确定数组的尺寸,则以下方法将不起作用。但是,如果您确实知道尺寸,请不要使用反射。执行以下操作:

您可以比这更容易动态构建2d数组。

int x = //some value
int y = //some other value

String[][] arr = new String[x][y];

这将“动态”创建xy 2d数组。

答案 4 :(得分:1)

  

另外请注意,如果我们尝试这样的话会怎么样:

Object arr1 = Array.newInstance(Array.class, 2);
Object arr2 = Array.newInstance(String.class, 4);
Object arr3 = Array.newInstance(String.class, 4);
Array.set(arr1, 0, arr2);
...

不,您不能设置String[]这样的值。你遇到了

Exception in thread "main" java.lang.IllegalArgumentException: array element type mismatch
at java.lang.reflect.Array.set(Native Method)
at Test.main(Test.java:12)

答案 5 :(得分:1)

有效的Java项目#(我不记得):了解并使用这些库。

您可以使用List并使用toArray方法:

List<String[]> twoDimension = new ArrayList<String[]>();

要将其转换为您将使用的数组:

String [][] theArray = twoDimension.toArray( new String[twoDimension.size()][] );

诀窍是,外部数组被声明为保存String[](字符串数组),而后者可以使用另一个List<String>动态创建,或者,如果您使用{{1解析字符串}} 方法。

<强>演示

专注于数组的动态创建,而不是解析,这是一个关于如何与String.split

结合使用的示例
String.split

有。现在,您的// and array which contains N elements of M size String input = "[[1],[2,3],[4,5,6,7],[8,9,10,11,12,13]]"; // Declare your dynamic array List<String[]> multiDimArray = new ArrayList<String[]>(); // split where ],[ is found, just ignore the leading [[ and the trailing ]] String [] parts = input.replaceAll("\\[\\[|\\]\\]","") .split("\\],\\["); // now split by comma and add it to the list for( String s : parts ){ multiDimArray.add( s.split(",") ) ; } String [][] result = multiDimArray.toArray( new String[multiDimArray.size()][]); 是一个二维动态创建的数组,其中包含:result

这是一个complete running demo,它还为混音添加了更多正则表达式,以消除空白区域。

我让你处理其他情况。

答案 6 :(得分:0)

所以我用代码来解决这个问题,从具有可变数量变量的多项式中提取系数。所以用户可能会 想要两个变量3 x^2 + 2 x y中的多项式的系数数组,或者它可以是一个有三个变量的数组。理想情况下,您需要一个用户可以轻松查询的多维数组,以便进行转换 整数[],整数[] []等。

这基本上使用与jdmichal的答案相同的技术,使用Array.newInstance(obj.getClass(), size)方法。对于多维数组,obj可以是一个较少维度的数组。

包含随机创建元素的示例代码

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;

public class MultiDimArray {
    static Random rand = new Random();

    /**
     * Create an multi-dimensional array 
     * @param depth number of dimensions
     * @return
     */
    static Object buildArray(int depth) {
        if(depth ==1) { // For 1D case just use a normal array
            int size = rand.nextInt(3)+1;
            Integer[] res = new Integer[size];
            for(int i=0;i<size;++i) {
                res[i] = new Integer(i);
            }
            return res;
        }
        // 2 or more dimensions, using recursion 
        int size = rand.nextInt(3)+1;
        // Need to get first items so can find its class
        Object ele0 = buildArray(depth-1);
        // create array of correct type
        Object res = Array.newInstance(ele0.getClass(), size);
        Array.set(res, 0, ele0);
        for(int i=1;i<size;++i) {
            Array.set(res, i, buildArray(depth-1));
        }
        return res;
    }

    public static void main(String[] args) {
        Integer[] oneD = (Integer[])  buildArray(1);
        System.out.println(Arrays.deepToString(oneD));

        Integer[][] twoD = (Integer[][])  buildArray(2);
        System.out.println(Arrays.deepToString(twoD));

        Integer[][][] threeD = (Integer[][][])  buildArray(3);
        System.out.println(Arrays.deepToString(threeD));
    }
}