a[i][j] 和 a[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[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] 有什么区别?的更多相关文章
- 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 ...
- 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 ...
- 组合数性质求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, ...
- 数组问题:a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头--数组赋值: int LEN = 10000; int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; i+ ...
- Eclipse中Java文件图标由实心J变成空心J的问题
在eclipse中空心J的java文件,表示不被包含在项目中进行编译,而是当做资源存在项目中.例如 当是单个文件为空心J的时候 1.右击该文件 -- >BuildPath -->Inclu ...
- 暑假集训#2 div1 J 四点直角 J - Space Invader 四点共面+跨立实验
题意:给你四个点,判断能否先依次通过A,B两点,然后再在某个地方只进行一次直角转弯再一次经过C,D两点: #include <iostream> #include <cstdio&g ...
- 面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]
第一种方法: 用两重循环对每对点都试一下,然后取最大值即可,时间复杂度为O(n2) #include <iostream> #include <algorithm> using ...
- 2013成都网络赛 J A Bit Fun(水题)
A Bit Fun Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- Trie URAL 7192 Chip Factory (15长春J)
题目传送门 题意:从n个数中选出不同的三个数a b c,使得(a+b)^c 最大 分析:先将所有数字按位插入到字典树上,然后删除两个数字,贪心询问与剩下的数字最大异或值. /************* ...
随机推荐
- yum install nginx-没有可用软件包 nginx。
1. 错误提示 Centos 7下安装nginx,使用yum install nginx,报错提示没有可用的软件包.具体错误提示如下: 已加载插件:fastestmirror, product-id, ...
- docker 添加Portainer容器图形化管理工具
主要参照了这边博客,但还是有些问题https://www.cnblogs.com/Bug-Hunter/p/12023130.html 比如端口9000得开启,docker端口映射得开启,得开启ip4 ...
- OpenShift添加应用健康检查功能
什么是健康检查? 对于部署成功的应用来说,通过访问接口.执行特定命令等方式判断应用是否存活.正常的方式称为健康检查. 在 OpenShift 或 Kubernetes 中,健康检查都有两个探针,分别是 ...
- 逆袭故事|从“点点点”到月薪17K,这个女孩子做到啦
姓:XXX,XX VIP 学员,一名自动化测试工程师!之前在某大型培训机构转行学习软件测试时就是木子老师的学生,她也一直关注木子老师的动态,后来知道木子老师打算开设测试开发高级提升班,等开了班就报名了 ...
- VMware虚拟机安装Win7填坑
今天本想赶紧安装Win7虚拟机做个实验,结果鼓捣了一天,写个文章填下坑. 一.Win7镜像文件下载 下载ISO镜像地址:http://msdn.itellyou.cn/ 二.安装tools VMwar ...
- Qt QChart 创建图表
Qt QChart 创建图表 @ 目录 Qt QChart 创建图表 效果 流程 代码 1. 饼图 2. 柱图 3. 折/曲线图 4. 区域图 效果 流程 graph LR q(value 数据) q ...
- SpringBoot2 整合OAuth2组件,模拟第三方授权访问
本文源码:GitHub·点这里 || GitEE·点这里 一.模式描述 授权服务 验证第三方服务的身份,验证邮箱用户的身份,记录和管理认证Token,为资源服务器提供Token校验.场景:第三方网站借 ...
- Docker实例开机启动
部署项目服务器时,为了应对停电等情况影响正常web项目的访问,会把Docker容器设置为开机自动启动. 在使用docker run启动容器时,使用--restart参数来设置: docker run ...
- JavaWeb基础总结:Servlet专题
最近工作中有部分整改老接口的任务,大部分与Spring的拦截器,Tomcat相关,改到一些底层的代码发现,对基础J2EE的知识有些遗忘,需要频繁查阅,索性从头系统的整理一下Servlet和Filter ...
- MySQL如何计算统计redo log大小
在MySQL中如何计算.统计重做日志(redo log)的生成情况呢? 例如10分钟内,生成了多少M的redo log呢?30分钟内又生成了多少M的redo log......MySQL没有像Or ...