`
RednaxelaFX
  • 浏览: 3015168 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

Java的多维数组

阅读更多
继续打捞站内信。

同学F 写道
在java中的数组访问,举个例子,对于数组int[][][] arry = new int[2][3][4],我从字节码上看,虚拟机对某个arry中的某个元素如arry[1][1][3]的访问,似乎是先获取arry[1]的引用,然后再获取arry[1][1]的引用,再获取数据arry[1][1][3],如果这个过程我没有理解错的话,那么虚拟机是不是对这些“中间引用”(arry[1]、arry[1][1]之类的)创建相应的类型,否则单凭这些引用如何进行数组下表的越界校验?


Java和JVM里本来就没有所谓的“矩形数组”的概念,多维数组只有“数组的数组”(array-of-arrays)或者叫jagged array。
与之对比,C#和CLI里就有真正的多维“矩形”数组,也支持“数组的数组”。关于几种不同的语言里多维数组的差异,可以参考以前一帖

也就是说,在Java里
类型 说明
int 这是一个基本类型
int[] 这是以int为元素的数组类型
int[][] 这是以int[]为元素的数组类型
int[][][] 这是以int[][]为元素的数组类型

一个数组类型的“组件类型”(component type)就是该数组类型的维度(dimension)减去1的类型;字面上看也就是少一对[]。

每个维度上都是一个真正的数组对象。每个数组对象都记录着自己的长度(length)。所以对每个数组对象都可以用arraylength指令去查询它们的长度,每个Xaload / Xastore也就可以做相应的边界检查。

int[][][] array = new int[2][3][4];

这只是个简写而已。虽然这个语句的右手边对应与一组JVM字节码指令,
   0:	iconst_2
   1:	iconst_3
   2:	iconst_4
   3:	multianewarray	#2,  3; //class "[[[I"

实际上它的作用大致等同于:
int[][][] a = new int[2][][];
for (int i = 0; i < a.length; i++) {
  a[i] = new int[3][];
  for (int j = 0; j < a[i].length; j++) {
    a[i][j] = new int[4];
  }
}


以32位HotSpot VM的实现为例,上面两个版本的代码创建出来的对象都是这种样子的:



这样就好理解了吧?

可以参考JVM规范去了解multianewarray的语义:
http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc9.html#multianewarray

HotSpot VM的实现里,参考解释器版的实现比较直观,在这里:
interpreterRuntime.cpp: InterpreterRuntime::multianewarray
objArrayKlass.cpp: objArrayKlass::multi_allocate
typeArrayKlass.cpp: typeArrayKlass::multi_allocate

该指令接受两个参数,
第一个是多维数组的类型,这个例子是里[[[I,也就是int[][][];
第二个是多维数组的维度n,这个例子里n=3。
知道了维度之后,JVM执行这条指令时就会从操作数栈顶弹出n个值(必须都是int型),并以这些int为每个维度的length嵌套循环的去创建数组对象出来;这个例子里也就是把前面iconst_2、iconst_3、iconst_4指令压到操作数栈上的常量2、3、4分别弹出来,并且作为数组的各维度的长度去创建实例。

multianewarray与上面写的那种显式用嵌套循环来初始化多维数组的Java代码最大的差异是,multianewarray会检查所有维度上的length是否非负,如果有负数就会抛NegativeArraySizeException;要注意的是无论传入的多维数组是否有维度的长度是0,所有维度都会被检查
而显然,如果显式用嵌套循环来初始化的话,负数长度的问题就有可能“逃过去”。

看例子:
public class Foo {
  public static void main(String[] args) {
    int[][][] array = new int[1][0][-1];
  }
}

这个会抛NegativeArraySizeException异常:
$ java Foo
Exception in thread "main" java.lang.NegativeArraySizeException
        at Foo.main(Foo.java:3)



public class Bar {
  public static void main(String[] args) {
    int[][][] a = new int[1][][];
    for (int i = 0; i < a.length; i++) {
      a[i] = new int[0][];
      for (int j = 0; j < a[i].length; j++) { // a[i].length == 0
        a[i][j] = new int[-1];                // 这个循环体不会被执行,-1的长度就被“跳过去”了
      }
    }
  }
}

这个不会抛异常。
  • 大小: 179.1 KB
分享到:
评论
5 楼 HanyuKing 2019-01-29  
   
4 楼 RednaxelaFX 2011-05-31  
IcyFenix 写道
讨论一下文中一个字面翻译的问题:
RednaxelaFX 写道
一个数组类型的“元素类型”(component type)就是该数组类型的维度(dimension)减去1的类型;字面上看也就是少一对[]。

component type是否翻译成“组件类型”会恰当一些?因为数组里面还有个element type表示“所有[]都去掉”的那个类型,把“元素类型”留给它会不会好一些?
JVMS 3,chapter 2.4  写道
An array type consists of a component type with a single dimension (whose length is not given by the type). The component type of an array type may itself be an array type. If, starting from any array type, one considers its component type, and then (if that is also an array type) the component type of that type, and so on, eventually one must reach a component type that is not an array type; this is called the element type of the array type

嗯…“组件类型”听起来怪怪的,不过就先用它吧 ^^
3 楼 IcyFenix 2011-05-31  
讨论一下文中一个字面翻译的问题:
RednaxelaFX 写道
一个数组类型的“元素类型”(component type)就是该数组类型的维度(dimension)减去1的类型;字面上看也就是少一对[]。

component type是否翻译成“组件类型”会恰当一些?因为数组里面还有个element type表示“所有[]都去掉”的那个类型,把“元素类型”留给它会不会好一些?
JVMS 3,chapter 2.4  写道
An array type consists of a component type with a single dimension (whose length is not given by the type). The component type of an array type may itself be an array type. If, starting from any array type, one considers its component type, and then (if that is also an array type) the component type of that type, and so on, eventually one must reach a component type that is not an array type; this is called the element type of the array type
2 楼 RednaxelaFX 2011-05-19  
IcyFenix 写道
这个例子小小滴补充一下,避免有同学误以为multianewarray指令会抛NegativeArraySizeException,而使用anewarray和newarray组合就不会抛NegativeArraySizeException。

嗯完全正确。多谢补充
1 楼 IcyFenix 2011-05-19  
public class Bar {
  public static void main(String[] args) {
    int[][][] a = new int[1][][];
    for (int i = 0; i < a.length; i++) {
      a[i] = new int[0][];
      for (int j = 0; j < a[i].length; j++) {
        a[i][j] = new int[-1];
      }
    }
  }
}
这个例子小小滴补充一下,避免有同学误以为multianewarray指令会抛NegativeArraySizeException,而使用anewarray和newarray组合就不会抛NegativeArraySizeException。

这2个指令也会抛出NegativeArraySizeException,只不过这个异常并非Linking Exception,换句话说,代码不执行到它就没有问题。

所以请同学们看清楚,撒迦使用的是:
int[][][] array = new int[1][0][-1]; 
而不是
int[][][] array = new int[1][1][-1]; 
或者
int[][][] array = new int[1][-1][-1]; 
后面2条用anewarray和newarray来写也会抛异常。

相关推荐

Global site tag (gtag.js) - Google Analytics