【问题标题】:Getting unknown number of dimensions from a multidimensional array in Java从Java中的多维数组中获取未知数量的维度
【发布时间】:2014-06-07 11:49:23
【问题描述】:

在 Java 中是否可以找到具有“先验”未知维数的数组的维数?也就是说,如果不知道多维矩阵的维数,如果可能的话,如何检索它?

【问题讨论】:

  • 您的意思是如何区分int[]int[][][][][]
  • 如果你不知道它的类型,你是如何接收这个数组的?就像Object?
  • 对!您可以将其作为对象或一些可变的用户输入来接收。情况是,真实的维数是未知的。 (不要将其与任何行、列或更复杂的几何/结构中的元素数量混淆)。我的兴趣是确定矩阵/超矩阵中不同维度的数量。
  • 澄清一下:维度的数量对我们来说是未知的,但显然,它必须在其他环境或上下文中知道(例如,用户的观点)。重要的一点是,我们在不知道它的维度数量的情况下接收它,即使它们在运行之前可能是未知的。如何确定?谢谢!

标签: java arrays multidimensional-array dimensions


【解决方案1】:

有谁知道在 Java 中是否可以获取具有“先验”未知维数的数组的维数?也就是说,如果不知道多维矩阵的维数,如果可能的话,如何检索它?

我不确定我是否正确理解了您要完成的工作。如果您只想知道数组中有多少个元素,Anubian 的答案是正确的。但我的理解是,你想计算给定通用数组的维数。

public class Test {
    public static int getNumberOfDimensions(Class<?> type) {
        if (type.getComponentType() == null) {
            return 0;
        } else {
            return getNumberOfDimensions(type.getComponentType()) + 1;
        }
    }

    public static void main(String[] args) {
        System.out.println(getNumberOfDimensions(int[][][].class)   == 3);
        System.out.println(getNumberOfDimensions(int[][].class)     == 2);
        System.out.println(getNumberOfDimensions(int[][][][].class) == 4);
        System.out.println(getNumberOfDimensions(int.class)         == 0);
    }
}

如果这不是您想要的,我会提示:lengthdimension 之间存在差异。


更新:我认为这与我们被问到的完全无关,但 Nicola 在 cmets 中问我:

这很好用,但是如果在运行时定义了维度的数量(例如,用户必须输入所需的维度数量)呢?如何定义和初始化数组?

解决方案在于一些基于光反射的黑客攻击:

import java.lang.reflect.Array;

public class Test {
    public static Class<?> getArrayType(Class<?> componentType, int dimensions) throws ClassNotFoundException {
        if (dimensions == 0) {
            return componentType;
        }

        String rawName = componentType.getName();
        switch (rawName) {
            case "byte":    rawName = "B"; break;
            case "char":    rawName = "C"; break;
            case "double":  rawName = "D"; break;
            case "float":   rawName = "F"; break;
            case "int":     rawName = "I"; break;
            case "long":    rawName = "J"; break;
            case "short":   rawName = "S"; break;
            case "boolean": rawName = "Z"; break;
            default:
                rawName = "L" + rawName + ";";
                break;
        }

        for (int i = 0; i < dimensions; i++) {
            rawName = "[" + rawName;
        }

        return Class.forName(rawName);
    }

    public static Object createArray(Class<?> componentType, int dimensions, int length) throws NegativeArraySizeException, ClassNotFoundException {
        if (dimensions == 0) {
            return null;
        }

        Object array = Array.newInstance(getArrayType(componentType, dimensions - 1), length);

        for (int i = 0; i < length; i++) {
            Array.set(array, i, createArray(componentType, dimensions - 1, length));
        }

        return array;
    }

    public static void main(String[] args) throws ClassNotFoundException {
        Object object = createArray(Integer.class, 3, 10);
        System.out.println(object.getClass());
    }
}

诀窍是使用给定的组件类型为 N 维数组构造一个类。如果我们知道类名是如何存储在最低级别的,我们就可以做到这一点。其余代码只是一个简单无趣的递归。

【讨论】:

  • 这很好用,但是如果在运行时定义了维度的数量(例如用户必须输入所需的维度数量)呢?如何定义和初始化数组?
  • 我不认为这是我们被问到的,但使用反射是可能的。
  • @Nicola 我更新了我的答案以包含您的问题。
  • 好的,使用反射来解决这个问题很好。我在这个领域没有经验,但也许反思不是最有效的方式,但无论如何从理论和概念的角度来看它是有效的。一个可能的改进是让长度参数本身成为一个数组:稍加调整就可以具有为每个维度定义不同大小的功能。
  • 是的,当然,但我想这是每个人都可以改变的。顺便说一句:我认为维数未知的数组没有真正的用例。我不记得我是否曾经使用过三维数组,现在我们正在谈论 N 维数组......这完全是一个理论任务,没有实际用途。但既然有人问,我就回答了。破解这样的解决方案总是很有趣!
【解决方案2】:

假设你的矩阵叫做 m:

可以通过运行得到列数

m.length;

在 Java 中,多维数组实际上是数组的数组。行数是可变的。假设 i 是介于 0 和 m.length-1 之间的整数,您可以这样做:

m[i].length;

获取行中的元素个数。

【讨论】:

  • 你是对的,但你不能无限期地继续这种方法:假设你有第三或第四维度......或者更复杂的假设你不知道实际上有多少尺寸(如问题中所述):在我看来,这种“硬编码”的方法是不可行的
【解决方案3】:

这里最初的问题是,您不能从以下内容开始说:

int getNumDimensions(int[] array) { ... }

因为如果theArray 实际上是int[][]int[][][] 等,这将失败。但是Java 中的所有数组都是Object 子类,所以这可以适用于任意数量的维度:

int getNumDimensions(Object array) {zz
    if (array instanceof Object[]) {
        // array has at least 2 dimensions (e.g. int[][])
        return getNumDimensions(((Object[]) array)[0]) + 1;
    }
    if (array.getClass().isArray()) {
        // array is, by process of elimination, one-dimensional
        return 1;
    }
    return 0;
}

编辑:我已经为那些疯狂的百万维数组尝试了非递归解决方案!

int getNumDimensions(Object array) {
    Object tmp = array;
    int nDimensions = 0;
    while (True) {
        if (array instanceof Object[]) {
            // tmp has at least 2 dimensions (e.g. int[][])
            tmp = ((Object[]) array)[0];
        }
        else if (tmp.getClass().isArray()) {
            // tmp is, by process of elimination, one-dimensional
            return nDimensions + 1;
        }
        nDimensions++;
    }
}

【讨论】:

  • 我目前没有可用的 JDK,所以如果此代码失败,请告诉我。
  • 代码中存在一些编译和逻辑错误。我已经编辑以更正这些。
  • 我在编辑的代码中也指出,如果任何维度的大小为 0,则无法继续计算该维度之后的任何维度。事实上,如果数组是通过multianewarray 分配的,则甚至不会分配任何进一步的维度。根据 JVM 规范,If any count value is zero, no subsequent dimensions are allocated.
  • 另外值得注意的是,由于实现只检查索引 0,锯齿状数组的结果可能与预期不同。考虑new Object[]{new int[2], new int[3][1][2]}。结果将是 2,但如果实现从 Object[] 检查索引 1,它会计算 4。但也许在这种情况下,询问维数甚至没有意义!
【解决方案4】:

我可以建议您通过使用只有一维的数组来解决此问题。假设有 N 个尺寸的维度:

然后您可以将数组的大小定义为:

   int size = S_0*S_1*...*S_N-1;

然后你将初始化它,例如一个整数数组:

int[] array = new int[size];

然后要引用数组中的一项(读取或写入),您有 N 个索引(因为有 N 个维度):

您现在需要的是一种方法,将这 N 个索引转换为一个可以用作一维数组中索引的单个值,通用公式为:

基本上,这个公式会“跳过”正确数量的先前维度并指向正确的位置。这个公式可以简单地用类似的方法实现

int read(int[] array, int[] indexes);
void write(int[] array, int[] indexes, int values);

为了保持代码的可读性。 您也可以使用泛型类型来实现此技术,因此这些方法可能适用于任何类型。

【讨论】:

  • 一个可行的替代方案,虽然它不能用于确定提问者在寻找什么。
  • 我明白你的意思,我的意思是:你怎么能最终陷入问题中描述的情况,你获得一个数组(例如作为一个返回值方法或方法参数),而不在代码中定义维数?我看不出你怎么会遇到这样的情况(在 C 中会有所不同,通过使用指针 int* 数组,它可以指向任何维度的数组,但这里我们在 Java 中),所以可能是提问者本人必须实现一种方法来处理这个问题。
【解决方案5】:

通过直接查看初始化它的代码或调用其类并检查其反射属性来查看其动态类型。

【讨论】:

  • 这可以扩展以查找以int 形式获取维数吗?
  • a.getClass() 将返回维数作为类的名称,例如int[][] 将返回“类 [[I”] 的名称。但是,如果您需要动态地这样做,那么您应该查看 Tomas 所展示的组件类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2021-01-13
  • 1970-01-01
  • 1970-01-01
  • 2019-11-11
  • 2015-08-13
  • 2012-11-28
相关资源
最近更新 更多