Java多维数组:内存地址和遍历速度
我知道在Java中处理二维数组时,访问循环中数组元素的顺序会影响遍历数组所需的时间:Java多维数组:内存地址和遍历速度,java,arrays,memory,Java,Arrays,Memory,我知道在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 <
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;
}
}
在大多数代码运行中,[i][j][k]
和[j][i][k]
的结果是可互换的。为什么会这样
另外,您能解释一下多维数组是如何存储在Java中的吗
给定数组int[][]array=new int[2][2][2]
,内存地址会是这样的吗(我的理解是,每个块之间可能有其他变量的附加数据,但我已经记录了这些情况,因为它们不相关):
(抱歉,如果图像是混乱的,我只有油漆,并尽力表达我的最佳布局。所以<代码>数组[0 ] [ 0 ] [ [ i] [j] [k] < /代码>将在地址<代码> 06 /代码> < p>第一个要考虑的是java,仔细地看,没有多维数组,因为它是一个单一的实体。相反,java只处理一维数组,但元素类型本身可以是数组类型,并且语言/编译器支持与C相同的语法,例如,多维度快捷寻址元素 例如,
int[]twoDim=newint[50][100]代码>实际在内存中创建51个对象;一个类型为int[][]
的数组,其中包含50个类型为int[]
的元素,并使用类型为int[100]
的数组填充这50个空间(生成剩余的50个对象)。这51个对象中的每一个都是独立的,它们可以位于堆中的任何位置。实际上,它们甚至不需要在同一个语句中创建
以下两种方法给出了相同的数组作为结果,但第二种方法应该清楚地说明引擎盖下到底发生了什么:
变量B和C的内存布局将不同,因为它们的分配顺序不同。但不要假设它们的内存布局是常量,垃圾收集器可能会在堆中移动它们
如果您担心访问速度,那么迭代数组的最快方法总是最左边的维度到最外面的循环,最右边的维度到最里面的循环(这是严格围绕单个维度在内存中线性定位这一事实构建的)。线性内存访问比随机访问的CPU速度更快(我不想在这里解释原因)
在处理阵列时,可以考虑两种微观优化
首先是尺寸的顺序,当您可以随意订购尺寸时,请将最小的最左边和最大的最右边放置:
第二个变量还节省了大量内存,因为慢变量包含10000 x int[2]=10001个对象,而快变量包含2 x int[10000]=3个对象
第二种是处理数组维度的切片(这是代码不变移动的一种形式):
长和=0;
int[]fastary=newint[2][10000];
对于(inti=0;我是如何测量的?在Java中精确地测量这些东西的性能(甚至精确到“A比B好”)是非常非常困难的。我使用了System.nanoTime()
在每次循环之前和之后减去差异是的,不,这不会得到准确的结果。如果你想得到测量结果,可以使用JMH或卡尺,实际上可以告诉你A快/慢/等于B。数组的java实现的技术术语是锯齿数组。谢谢你的解释。我不想问理解存储数组数组数组背后的思想……就字节码而言,没有什么能阻止人们使用单个连续内存块来定义n维数组,从而获得更可预测的性能。@edTarik阻止你将多维数组定义为连续bl的东西内存块是java语言规范本身,特别是每个数组(也是)一个对象。通过createArrayB()示例执行步骤(实际上或更好的是在调试器中),并查看整个方法中的数组内容。此外,您可能希望检查术语“锯齿数组”(此功能也是阵列阵列组织的结果)。通过使用索引,您可以很容易地使用一维数组获得与2D数组等效的数组。C内部就是这样做的。因此,JVM中没有任何东西阻止向Java编程语言添加语法糖,以处理2D数组和更传统的锯齿数组。@edTarik我明白您的意思,但这仍然是正确的需要在语言级别上进行更改,而不仅仅是在虚拟机级别。仅使用(纯)语法sugar可以实现的功能是有限的(请看我的上一个代码示例,其中代码不变的移动说明了原因,这将是一个非常基本的添加)。您仍然可以通过自己进行索引数学(使用当前的java语言元素)来模拟经典的C多维数组,而实际上是将数据存储在一个一维数组中,如果你觉得这样做值得付出努力的话。我看了代码不变的代码。我仍然不太明白是什么阻止了使用添加Java语法构造来做到这一点:使用一维数组并使用未更改的JVM进行索引数学。
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])
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;
}
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;
}
int[][] slowArray = new int[10000][2];
int[][] fastArray = new int[2][10000];
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];
}
}