前几天,一个小伙伴告诉我,他在面试的时候被面试官问了这么一个问题:

在for循环中,到底应该用 i++ 还是 ++i ?

听到这,我感觉这面试官确实有点不按套路出牌了,放着好好的八股文不问,净整些幺蛾子的东西。在临走的时候,小伙伴问面试官这道题的答案是什么,面试官没有明确告诉答案,只是说让从程序执行的效率角度自己思考一下。

好吧,既然这个问题被抛了出来,那我们就见招拆招,也给以后面试的小伙伴们排一下坑。

思路

前面提到,这个搞事情的面试官说要从执行效率的角度思考,那我们就抛开语义上的区别,从运行结果以外的效率来找找线索。回想一下,我们在以前介绍CAS的文章中提到过,后置自增i++和前置自增++i都不是原子操作,那么实际在执行过程中是什么样的呢?下面,我们从字节码指令的角度,从底层进行一波分析。

i++ 执行过程

先写一段简单的代码,核心功能就只有赋值和自增操作:

public static void main(String[] args) {int i=3;int j=i++;System.out.println(j);
}

下面用javap对字节码文件进行反编译,看一下实际执行的字节码指令:

是不是有点难懂?没关系,接下来我们用图解的形式来直观地看看具体执行的过程,也帮大家解释一下晦涩的字节码指令是如何操作栈帧中的数据结构的,为了简洁起见,在图中只列出栈帧中比较重要的操作数栈局部变量表

上面的代码中除去打印语句,整体可以拆分成两步,我们先看第一步 int i=3 是如何执行的 。

上面两条操作数栈和局部变量表相关的字节码指令还是比较容易理解的,下面再看一下第二步int j=i++的执行过程:

在上图中需要注意的是,iinc能够直接更新局部变量表中的变量值,它不需要把数值压到操作数栈中就能够直接进行操作。在上面的过程中,抛去赋值等其他操作,i++实际执行的字节码指令是:

2: iload_1
3: iinc    1, 1

如果把它翻译成我们能看懂的java代码,可以理解为:

int temp=i;
i=i+1;

也就是说在这个过程中,除了必须的自增操作以外,又引入了一个新的局部变量,接下来我们再看看++i的执行过程。

++i 执行过程

我们对上面的代码做一点小小的改动,仅把i++换成++i,再来分析一下++i的执行过程是怎样的。

public static void main(String[] args) {int i=3;int j=++i;System.out.println(j);
}

同样,用javap反编译字节码文件:

int i=3对应前两行字节码指令,执行过程和前面i++例子中完全相同,可以忽略不计,重点还是通过图解的方式看一下int j=++i对应的字节码指令的执行过程:

抛去赋值操作,++i实际执行过程只有一行字节码指令:

2: iinc    1, 1

转换成能理解的java代码的话,++i实际执行的就在局部变量中执行的:

i=i+1;

这么看来,在使用++i时确实比i++少了一步操作,少引入了一个局部变量,如果在运算结果相同的场景下,使用++i的话的确效率会比i++高那么一点点。

那么回到开头的问题,两种自增方式应用在for循环中执行的时候,那种效率更高呢?刚才得出的结论仍然适用于for循环中吗,别急,让我们接着往下看。

for循环中的自增

下面准备两段包含了for循环的代码,分别使用i++后置自增和++i前置自增:

//i++ 后置自增
public class ForIpp {public static void main(String[] args) {for (int i = 0; i < 5; i++) {System.out.println(i);}}
}
//++i 前置自增
public class ForPpi {public static void main(String[] args) {for (int i = 0; i < 5; ++i) {System.out.println(i);}}
}

老规矩,还是直接反编译后的字节码文件,然后对比一下指令的执行过程:

到这里,有趣的现象出现了,两段程序执行的字节码指令部分居然一模一样。先不考虑为什么会有这种现象,我们还是通过图解来看一下字节码指令的执行过程:

可以清晰的看到,在进行自增时,都是直接执行的iinc,在之前并没有执行iload的过程,也就是说,两段代码执行的都是++i。这一过程的验证其实还有更简单的方法,直接使用idea打开字节码文件,就可以看到最终for循环中使用的相同的前置自增方式。

那么,为什么会出现这种现象呢?归根结底,还是java编译器对于代码的优化,在两种自增方式中,如果没有赋值操作,那么都会被优化成一种方式,就像下面的两个方法的代码:

void ipp(){int i=3;i++;
}
void ppi(){int i=3;++i;
}

最终执行时的字节码指令都是:

0: iconst_3
1: istore_1
2: iinc    1, 1
5: return

可以看到,在上面的这种特定情况下,代码经过编译器的优化,保持了语义不变,并通过转换语法的形式提高了代码的运行效率。所以再回到我们开头的问题,就可以得出结论,在for循环中,通过jvm进行编译优化后,不论是i++还是++i,最终执行的方式都是++i,因此执行效率是相同的。

所以,以后再碰到这种半吊子的面试官,和你谈for循环中i++++i的效率问题,自信点,直接把答案甩在他的脸上,两种方式效率一样!

本文代码基于Java 1.8.0_261-b12 版本测试

往期推荐

面试官:HashMap有几种遍历方法?推荐使用哪种?

面试官:重写 equals 时为什么一定要重写 hashCode?

List 去重的 6 种方法,这个方法最完美!

面试官又整新活,居然问我for循环用i++和++i哪个效率高?相关推荐

  1. 当面试官说“还有哪些问题需要问”该如何回答?

    很多面试官在面试结束前都会问"你有什么问题要问我?"这是求职者了解公司的重要机会,也可以利用这个时机表忠心,表决心,展现自己的优势.当面试官说"还有哪些问题需要问&quo ...

  2. 面试官:你还有什么问题要问的吗?

    面对HR或者其他Level比较低的面试官时 能不能谈谈你作为一个公司老员工对公司的感受? (这个问题比较容易回答,不会让面试官陷入无话可说的尴尬境地.另外,从面试官的回答中你可以加深对这个公司的了解, ...

  3. 面试官:“你还有什么想问我的吗?”(攻略版,建议收藏系列)

    面试官:"你还有什么想问我的吗?"(攻略版,建议收藏系列) 此类问题大致分为三种情形:部门负责人面.HR面.高层领导面. 一.部门负责人面 [判断自己的业务能力和性格特点是否与岗位 ...

  4. 面试官这Redis夺命连环12问,谁顶得住?

    面试官这夺命连环12问,谁顶得住? ⏬ 面试官: 同学,我看你每个项目中都用到了Redis,你能说说你是怎样使用Redis的吗? 小A同学: 主要用来做缓存,分布式Session, 阅读量/点赞数统计 ...

  5. 面试官,求你了别再问我TCP三次握手和四次挥手了(含面试题)

    少点代码,多点头发 三次握手建立链接,四次挥手断开链接.这个问题算非常经典的问题,也是面试官非常喜欢问的问题. 不夸张的说,龙叔在校招面试的时候每一家公司都问到过关于三次握手和四次挥手相关的问题,相信 ...

  6. 面试官:你还有什么要问的吗?

    很多面试官在面试结束前都会问,"你有什么问题要问我?" 这不仅仅是求职者了解公司的重要机会,如果很想加入的话,可以再次利用这个时机表忠心,表决心,展现自己的个人优势. 有些问题,说 ...

  7. 软件测试面试官:你有什么需要问我吗?小伙子一口气提了四个问题,被弃用!

    一说到面试,首先给大家一个小小的建议,: 首先我们在面试之前,一定要对公司的最基本的背景情况了解一下, 至少知道公司的产品,业务类型,金融,教育? 说明你下了功夫,真心求职,尊重和重视这次面试 互联网 ...

  8. Java面试题总结(乱序版,来自大厂面试官的MySQL灵魂十连问

    Synchronized是java语言的关键字,是原生语法层面的互斥,需要JVM实现:ReentrantLock 是JDK1.5之后提供的API层面的互斥锁,需要lock和unlock()方法配合tr ...

  9. 面试官:“你有什么要问我的吗?”问什么比较好?

    点击上面↑「爱开发」关注我们 每晚10点,捕获技术思考和创业资源洞察 当你在沟通过程中了解到想要的信息后,问不问其实意义不大,这时可以直接客气地说"我没有什么问题了",从而结束这场 ...

最新文章

  1. window.open的用法---javascript学习笔记
  2. [LCS]启用WindowsMessenger5.0的调试日志功能来诊断登录LCS服务器
  3. 自媒体时代的贤内助——AI 视频云
  4. numpy的基本使用2
  5. javafx中的tree_JavaFX中的塔防(5)
  6. Matlab Compiler路径
  7. 华为畅享max有没有人脸识别_华为三款5000mAh超大电池手机,都是续航王者,最低只要1499元...
  8. 一列对应右侧几行html,css查找表格第几行第几列
  9. 三个故事看懂了再结婚(转)
  10. 自考的那些事儿(三):软件开发工具(总述篇)
  11. Premiere Elements使用教程:将音乐添加到视频片段
  12. SQL Server 创建链接服务器
  13. 乐高机器人骨奥_乐高机器人这个大坑,为啥大家都拽着孩子往里跳?
  14. 2016最新精彩而又幽默的搞笑段子精选
  15. 旅行青蛙分析(Android篇)
  16. RenderTexture实现小地图和炫酷的传送门!(干货收藏)
  17. 弘辽科技:白象方便面也被野性消费了吗?
  18. K13252 [国王游戏2]
  19. 萌新问题合集:2.win10关于ftp保存了密码怎么取消?
  20. MalformedJsonException: JSON forbids octal prefixes

热门文章

  1. centos7php自启动,centos7系统下nginx安装并配置开机自启动操作
  2. python几何拼贴画_图形几何图形拼贴画
  3. mysql 横向扩展 中间件_mysql-proxy数据库中间件架构 | 架构师之路
  4. CentOS 5打造全功能Web服务器
  5. 介绍一下再Apache下的Tomcat负载均衡的一些使用问题
  6. Python 核心编程(第二版)——条件和循环
  7. 新的网站上线 linux视野
  8. as3:sprite作为容器使用时,最好不要指定width,height
  9. Cisco Adsl 详细配置
  10. 初学 Delphi 嵌入汇编[29] - 寄存器所能接受的数值范围