本文以一个简单的程序开头——数组赋值:

int LEN = 10000;
int[][] arr = new int[LEN][LEN];

for (int i = 0; i < LEN; i++) {
for (int j = 0; j < LEN; j++) {
arr[i][j] = 1;
}
}
示例中虽然采用了Java,但是熟悉其他编程语言的同学可以自行脑补成自己熟悉的语言,如C/C++、Go、Python之类的,这里的知识点不限制在语言层级。

我们在使用这种for循环的时候,是否会习惯性地使用arr[i][j]的这种写法? 其实很多开源代码、技术书籍示例中都是如此。你是否还考虑过还有如下这种写法,即将 arr[i][j] 替换成 arr[j][i] :

int LEN = 10000;
int[][] arr = new int[LEN][LEN];

for (int i = 0; i < LEN; i++) {
for (int j = 0; j < LEN; j++) {
arr[j][i] = 1;
}
}
两段代码功能完全一样,究竟有何区别?两者性能会有数十倍或者数百倍之差。

有些同学看到这里,直接祭出IDE,运行试了一下,发现第一段代码的性能最优,所以很快地得出了a[i][j]最优的结论。不过也会有同学会运行出第二段代码性能更优的结果,即a[j][i]最优。这到底是怎么回事呢?

从语言本身看,这个问题的要点在于语言怎样实现矩阵 arr[M][N] 的存放。有两种最基本的存放方式:一种是第1下标优先存放;另一种是第2下标优先存放。也就是一般所说的行优先(Row-major)和列优先(Column-major)。

举个例子,对于下面的数组:

可以有两种存储方式:左为列优先,右为行优先。

行优先存储,顾名思义,就是一行的数据存放在一起,然后逐行存放。列优先存储,就是每一列的数据是存储在一起的,一列一列地存放在内存中。这两种存储方法,对于编写遍历二维矩阵的循环语句,还是有一定影响的。比如,如果是按行优先存储的,那么在遍历时,一行一行的读取数据,肯定比一列一列地读取整个数组,要方便许多(前者连续访问内存,有利于CPU高速缓存,后者不联系访问内存,会引起频繁更新高速缓存)。

行优先(Row-major)或者列优先(Column-major)没有好坏,但其直接涉及到对内存中数据的最佳存储访问方式。因为在内存使用上,程序访问的内存地址之间连续性越好,程序的访问效率就越高;相应地,程序访问的内存地址之间连续性越差。所以,我们应该尽量在行优先机制的编译器,比如C/C++、Objective-C(for C-style arrays)、Pascal等等上,采用行优先的数据存储方式;在列优先机制的编译器,比如Fortune、Matlab、R等等上,采用列优先的数据存储方式。但这种思想渗透到编程中之后,代码的质量就会提高一个档次。

这里没有提到市场占有率很高的Java语言,那么它们属于行优先还是列优先呢?

答案:两者都不是。

密集数组存储的一个典型替代方案是使用伊利夫向量(Iliffe vector),它通常将元素存储在连续的同一行中(如同Row-major顺序),但不存储行本身。

什么是Iliffe向量?在计算机编程中,Iliffe向量是一种用于实现多维数组的数据结构。n维数组的Iliffe向量(其中n≥2)由指向(n - 1)维数组的指针的向量(或1维数组)组成。它们通常用于避免在对数组元素执行地址计算时执行昂贵的乘法操作。它们还可用于实现交错数组,如三角形数组、三角形矩阵和其他各种不规则形状的数组。数据结构以John K. Iliffe命名。

它们的缺点包括需要多个链接指针来访问一个元素,以及需要额外的工作来确定n维数组中的下一行,以允许优化编译器预取它。在CPU明显快于主存的系统中,这两个问题都是延迟的来源。

在Java、Python(多维列表)、Ruby、Visual Basic . net、Perl、PHP、JavaScript、Objective-C(当使用NSArray时,不是一个Row-magor C-Style的数组)、Swift和Atlas Autocode等语言中的多维数组被实现为Iliffe向量。利用Iliffe向量实现OLAP产品全息的稀疏多维阵列。

回到开头,如果使用Java语言运行文中开头的2段代码,哪个更叫高效呢?请在留言区留下你的答案和思考。

a[i][j] 和 a[j][i] 有什么区别?的更多相关文章

  1. GCD - Extreme (II) for(i=1;i<N;i++) for(j=i+1;j<=N;j++) { G+=gcd(i,j); } 推导分析+欧拉函数

    /** 题目:GCD - Extreme (II) 链接:https://vjudge.net/contest/154246#problem/O 题意: for(i=1;i<N;i++) for ...

  2. Codeforces Round #191 (Div. 2) A. Flipping Game【*枚举/DP/每次操作可将区间[i,j](1=<i<=j<=n)内牌的状态翻转(即0变1,1变0),求一次翻转操作后,1的个数尽量多】

    A. Flipping Game     time limit per test 1 second memory limit per test 256 megabytes input standard ...

  3. 组合数性质求K个数选取i*j个数分成j组的方案数

    分析:设方案数为ANS,C代表组合数: ANS=(C[K,I]*C[K-I,I][K-2*I,I]*...*C[K-(J-1)*I,I])/(J!); 也即: ANS=C[K,I*J]*(C[I*J, ...

  4. 数组问题:a[i][j] 和 a[j][i] 有什么区别?

    本文以一个简单的程序开头--数组赋值: int LEN = 10000; int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; i+ ...

  5. Eclipse中Java文件图标由实心J变成空心J的问题

    在eclipse中空心J的java文件,表示不被包含在项目中进行编译,而是当做资源存在项目中.例如 当是单个文件为空心J的时候 1.右击该文件 -- >BuildPath -->Inclu ...

  6. 暑假集训#2 div1 J 四点直角 J - Space Invader 四点共面+跨立实验

    题意:给你四个点,判断能否先依次通过A,B两点,然后再在某个地方只进行一次直角转弯再一次经过C,D两点: #include <iostream> #include <cstdio&g ...

  7. 面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]

    第一种方法: 用两重循环对每对点都试一下,然后取最大值即可,时间复杂度为O(n2) #include <iostream> #include <algorithm> using ...

  8. 2013成都网络赛 J A Bit Fun(水题)

    A Bit Fun Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  9. Trie URAL 7192 Chip Factory (15长春J)

    题目传送门 题意:从n个数中选出不同的三个数a b c,使得(a+b)^c 最大 分析:先将所有数字按位插入到字典树上,然后删除两个数字,贪心询问与剩下的数字最大异或值. /************* ...

随机推荐

  1. Jmeter(10)逻辑控制器

    逻辑控制器可以控制采样器的执行顺序,所以控制器需要和采样器一起使用 Jmeter中的逻辑控制器分为两类 1.控制测试计划执行过程中节点的逻辑顺序,如循环控制器.If控制器 2.对测试计划中的脚本进行分 ...

  2. oracle查年度周末日期

    1.查年度周末日期sql SELECT distinct TRUNC(TO_DATE('2019-01-01','yyyy-mm-dd')+ rownum,'iw')+ 5 AS sat, TRUNC ...

  3. php项目从github自动pull到服务器

    php项目github自动pull到服务器 项目名:web 一.自动触发 1.在服务器添加脚本文件:gitpull.sh #!/bin/sh cd /www/web git reset --hard ...

  4. Spark Connector Reader 原理与实践

    本文主要讲述如何利用 Spark Connector 进行 Nebula Graph 数据的读取. Spark Connector 简介 Spark Connector 是一个 Spark 的数据连接 ...

  5. [日常摸鱼]poj2420 A Star not a Tree?

    题意:给定$n$个点,找一个点使得这个点到所有点的距离之和最小,求出这个最小距离 传说中的模拟退火- #include<cstdio> #include<ctime> #inc ...

  6. 使用gitlab-runner本地验证.gitlab-ci.yml

    背景 在gitlab上配置新项目的CI的时候,需要编写项目的 .gitlab-ci.yml 文件. 每次修改 .gitlab-ci.yml 文件之后都要执行git push让GitLab去构建来验证当 ...

  7. [UWP] - Adaptive Trigger Featurede的使用方法及效果

    XAML代码: <Page.Resources> <!-- DataTemplate to use in the portrait layout. --> <DataTe ...

  8. 【程序包管理】Linux程序包管理之yum源安装

    yum源安装是我们工作中常用的一种方式,它是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载RP ...

  9. android 使用 Lottie

    1.添加依赖 dependencies { implementation 'com.airbnb.android:lottie:2.5.5'//lottie } 2.1layout实现 <?xm ...

  10. MySQL建立索引遵循原则的注意点

    1.选择唯一性索引 唯一性索引的数据是唯一的,可以更快的通过该索引查询某条数据. 2.为经常需要排序,分组和联合操作的字段建立索引 order by,group by的字段在排序操作时很是耗时,可以对 ...