欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java的多维数组

程序员文章站 2022-06-04 14:08:24
...
继续打捞站内信。

同学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的实现为例,上面两个版本的代码创建出来的对象都是这种样子的:
Java的多维数组
            
    
    博客分类: JavaHotSpot VMVirtual Machine JavaJVMOracleITeyeJ# 


这样就好理解了吧?

可以参考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的长度就被“跳过去”了
      }
    }
  }
}

这个不会抛异常。
  • Java的多维数组
            
    
    博客分类: JavaHotSpot VMVirtual Machine JavaJVMOracleITeyeJ# 
  • 大小: 179.1 KB