据我所知,在Java中处理二维数组时,在循环中访问数组元素的顺序会影响遍历数组所需的时间:
int size = 500;
int[][] array = new int[size][size];
// Slower
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
array[j][i] = 1;
}
}
// Faster
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
array[i][j] = 1;
}
}
这对我来说很有意义,因为它需要较少的内存跳跃,而只能步入后续地址。
当使用三维数组进行相同操作时,我对结果更加困惑:
Time: 12356332 nanoseconds. ([i][j][k])
Time: 18278948 nanoseconds. ([i][k][j])
Time: 13985288 nanoseconds. ([j][i][k])
Time: 126192723 nanoseconds. ([j][k][i])
Time: 39441820 nanoseconds. ([k][i][j])
Time: 156352618 nanoseconds. ([k][j][i])
[i][j][k]
和[j][i][k]
的结果在大多数代码运行中都是可以互换的。这是为什么?
另外你能解释一下多维数组是如何存储在Java中的吗?
给定数组int[][][] array = new int[2][2][2]
,内存地址是这样的(我的理解是每个块之间可能有其他变量的附加数据,但我已经省略了这些情况,因为它们不相关):
(很抱歉,如果图片令人困惑,我只能使用油漆,并尽力表达布局。所以array[0][0][0] //[i][j][k]
会在地址06
)
答案 0 :(得分:3)
首先要考虑的是,java在其单一实体的意义上具有密切关注的无多维数组。相反,java仅处理单维数组,但元素类型本身可以是数组类型,并且语言/编译器支持与例如相同的语法。 C可用于多个维度以快速寻址元素。
例如,int[][] twoDim = new int[50][100];
实际上在内存中创建了51个对象; 一个类型为int[][]
的数组,其中包含50个int[]
类型元素的空间,并使用int[100]
类型的数组填充这50个空格(剩下的50个)对象)。这51个对象中的每一个都是相同的,它们可以位于堆中的任何位置。实际上,他们甚至不需要在同一个声明中创建。
下面两个方法给出了与结果相同的数组,但第二个方法应该明确真正
public int[][] createArrayA(int n, int m) {
return new int[n][m];
}
public int[][] createArrayB(int n, int m) {
int[][] array = new int[n][];
for (int i=0; i<n; ++i)
array[i] = new int[m];
return array;
}
请注意,在createArrayB()中,您可以选择向后初始化n维(循环倒计数而不是向上),从而导致相同的数组:
public int[][] createArrayC(int n, int m) {
int[][] array = new int[n][];
for (int i=n-1; i>=0; --i)
array[i] = new int[m];
return array;
}
变体B和C的内存布局会有所不同,因为它们的分配顺序不同。但是不要以为它们的内存布局是一个常量,垃圾收集器可能会在堆中将它们放在堆中。
如果你担心访问速度,迭代数组的最快方法是最左边的维度进入最外层循环, 最右边的维度转到最里面的循环(这严格围绕各个维度线性位于内存中的事实)。线性内存访问的CPU比随机访问更快(我不会进入为什么)。
在处理数组时,可以考虑可以进行两次微优化。
首先是尺寸的顺序,当您可以随意订购尺寸时,将最小最左侧和最大最右侧放置:
int[][] slowArray = new int[10000][2];
int[][] fastArray = new int[2][10000];
第二个还节省了大量内存,因为慢变量由10000 x int [2] = 10001个对象组成,而快速变体由2 x int [10000] = 3个对象组成。
第二个是使用数组维度的切片(它是代码不变运动的一种形式):
long sum = 0;
int[][] fastArray = new int[2][10000];
for (int i=0; i<fastArray.length; ++i) {
int[] subArray = fastArray[i];
for (int j=0; j<subArray.length; ++j) {
sum += subArray[j];
}
}
定义局部变量subArray完全从内部循环中消除外部维度(毕竟,我从不在内部循环中更改,所以为什么每次要解析j时都要查找数组索引?)。这种优化可以由即时编译器自动执行,但据我所知,它不会自动执行总是。偶尔的循环并不重要,但是如果数组行走是你处理时间的重要部分,那么就需要考虑优化。