web-dev-qa-db-ja.com

Javaで多次元配列を動的に構築することは可能ですか?

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は次と同等の2D配列になります。

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

等々。オブジェクトの巨大なネストされた配列である必要があります

25
Jordan

実際にはJavaで行うことができます。 (私は言わなければならない少し驚いています。)

免責事項;この質問への回答として以外に、このコードを見たくはありません。 Listsを使用することを強くお勧めします。

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]]]
21
aioobe

配列はJava-単純な配列と「多次元」配列(つまり、配列の配列)に適用されます)でタイプセーフです。

ネストの深さが実行時に可変である場合、実行できる最善の方法は、既知の最小ネスト深度(おそらく1)に対応する配列を使用することです。この配列の要素は、単純な要素であるか、さらにネストする場合に使用します。別の配列が必要です。ネストされた配列自体もオブジェクトと見なされ、型システム内に収まるため、Object []配列を使用するとこれを実行できます。

ネストが完全に規則的である場合は、Array.newInstance(String.class, dimension1, dimension2, ...)を使用して、この規則性をプリエンプトし、適切な多次元配列を作成できます。ネストが不規則な場合は、「ギザギザ」を許容するネストされたリストを使用することをお勧めします。構造と動的サイジング。ジェネリックスを犠牲にして、ギザギザの構造を持つことができます。一部の要素は単純なアイテムであり、他の要素はさらにネストされたリストである可能性があるため、構造がギザギザになっている場合、ジェネリックは使用できません。

4
mdma

したがって、複数のディメンションを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]

もう1つのオプションは、前のレベルの配列の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
jdmichal

配列の次元がわからない場合は、次の方法は機能しません。ただし、寸法がわかっている場合は、反射を使用しないでください。以下をせよ:

それよりもはるかに簡単に2D配列を動的に構築できます。

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

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

これにより、「動的に」x by y2d配列が作成されます。

1
jjnguy

さらに、次のようなことを試してみるとどうなるでしょうか。

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)
1
aioobe

効果的Java item#(覚えていない):ライブラリを知って使用してください。

ListtoArrayメソッドを使用できます。

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

それを配列に変換するには、次を使用します。

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

秘訣は、外側の配列がString[](文字列配列)を保持するように宣言されていることです。これは、別のList<String>で動的に作成できます。または、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は、予想どおり[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13]]を含む動的に作成された2次元の配列になります。

これが 完全な実行デモ です。これはまた、空白を排除するために、ミックスに正規表現を追加します。

他のシナリオを処理させます。

0
OscarRyz

そこで、変数の数が可変の多項式から係数を抽出するコードでこの質問に出くわしました。したがって、ユーザーは2つの変数_3 x^2 + 2 x y_の多項式の係数配列が必要な場合もあれば、3つの変数を持つ1つの場合もあります。理想的には、ユーザーが簡単に問い合わせることができ、Integer []、Integer [] []などにキャストできる多次元配列が必要です。

これは基本的に、Array.newInstance(obj.getClass(), size)メソッドを使用して、jdmichalの回答と同じ手法を使用します。多次元配列の場合、objは1次元少ない配列にすることができます。

ランダムに作成された要素を含むサンプルコード

_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));
    }
}
_
0
Salix alba