<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官又整新活,居然問我for循環(huán)用i++和++i哪個效率高?

          共 1645字,需瀏覽 4分鐘

           ·

          2021-11-28 14:17

          前幾天,一個小伙伴告訴我,他在面試的時候被面試官問了這么一個問題:

          在for循環(huán)中,到底應(yīng)該用 i++ 還是 ++i ?

          聽到這,我感覺這面試官確實(shí)有點(diǎn)不按套路出牌了,放著好好的八股文不問,凈整些幺蛾子的東西。在臨走的時候,小伙伴問面試官這道題的答案是什么,面試官沒有明確告訴答案,只是說讓從程序執(zhí)行的效率角度自己思考一下。

          好吧,既然這個問題被拋了出來,那我們就見招拆招,也給以后面試的小伙伴們排一下坑。

          思路

          前面提到,這個搞事情的面試官說要從執(zhí)行效率的角度思考,那我們就拋開語義上的區(qū)別,從運(yùn)行結(jié)果以外的效率來找找線索?;叵胍幌拢覀冊谝郧敖榻BCAS的文章中提到過,后置自增i++和前置自增++i都不是原子操作,那么實(shí)際在執(zhí)行過程中是什么樣的呢?下面,我們從字節(jié)碼指令的角度,從底層進(jìn)行一波分析。

          i++ 執(zhí)行過程

          先寫一段簡單的代碼,核心功能就只有賦值和自增操作:

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

          下面用javap對字節(jié)碼文件進(jìn)行反編譯,看一下實(shí)際執(zhí)行的字節(jié)碼指令:

          是不是有點(diǎn)難懂?沒關(guān)系,接下來我們用圖解的形式來直觀地看看具體執(zhí)行的過程,也幫大家解釋一下晦澀的字節(jié)碼指令是如何操作棧幀中的數(shù)據(jù)結(jié)構(gòu)的,為了簡潔起見,在圖中只列出棧幀中比較重要的操作數(shù)棧局部變量表。

          上面的代碼中除去打印語句,整體可以拆分成兩步,我們先看第一步 int i=3 是如何執(zhí)行的 。

          上面兩條操作數(shù)棧和局部變量表相關(guān)的字節(jié)碼指令還是比較容易理解的,下面再看一下第二步int j=i++的執(zhí)行過程:

          在上圖中需要注意的是,iinc能夠直接更新局部變量表中的變量值,它不需要把數(shù)值壓到操作數(shù)棧中就能夠直接進(jìn)行操作。在上面的過程中,拋去賦值等其他操作,i++實(shí)際執(zhí)行的字節(jié)碼指令是:

          2:?iload_1
          3:?iinc????1,?1

          如果把它翻譯成我們能看懂的java代碼,可以理解為:

          int?temp=i;
          i=i+1;

          也就是說在這個過程中,除了必須的自增操作以外,又引入了一個新的局部變量,接下來我們再看看++i的執(zhí)行過程。

          ++i 執(zhí)行過程

          我們對上面的代碼做一點(diǎn)小小的改動,僅把i++換成++i,再來分析一下++i的執(zhí)行過程是怎樣的。

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

          同樣,用javap反編譯字節(jié)碼文件:

          int i=3對應(yīng)前兩行字節(jié)碼指令,執(zhí)行過程和前面i++例子中完全相同,可以忽略不計(jì),重點(diǎn)還是通過圖解的方式看一下int j=++i對應(yīng)的字節(jié)碼指令的執(zhí)行過程:

          拋去賦值操作,++i實(shí)際執(zhí)行過程只有一行字節(jié)碼指令:

          2:?iinc????1,?1

          轉(zhuǎn)換成能理解的java代碼的話,++i實(shí)際執(zhí)行的就在局部變量中執(zhí)行的:

          i=i+1;

          這么看來,在使用++i時確實(shí)比i++少了一步操作,少引入了一個局部變量,如果在運(yùn)算結(jié)果相同的場景下,使用++i的話的確效率會比i++高那么一點(diǎn)點(diǎn)。

          那么回到開頭的問題,兩種自增方式應(yīng)用在for循環(huán)中執(zhí)行的時候,那種效率更高呢?剛才得出的結(jié)論仍然適用于for循環(huán)中嗎,別急,讓我們接著往下看。

          for循環(huán)中的自增

          下面準(zhǔn)備兩段包含了for循環(huán)的代碼,分別使用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);
          ????????}
          ????}
          }

          老規(guī)矩,還是直接反編譯后的字節(jié)碼文件,然后對比一下指令的執(zhí)行過程:

          到這里,有趣的現(xiàn)象出現(xiàn)了,兩段程序執(zhí)行的字節(jié)碼指令部分居然一模一樣。先不考慮為什么會有這種現(xiàn)象,我們還是通過圖解來看一下字節(jié)碼指令的執(zhí)行過程:

          可以清晰的看到,在進(jìn)行自增時,都是直接執(zhí)行的iinc,在之前并沒有執(zhí)行iload的過程,也就是說,兩段代碼執(zhí)行的都是++i。這一過程的驗(yàn)證其實(shí)還有更簡單的方法,直接使用idea打開字節(jié)碼文件,就可以看到最終for循環(huán)中使用的相同的前置自增方式。

          那么,為什么會出現(xiàn)這種現(xiàn)象呢?歸根結(jié)底,還是java編譯器對于代碼的優(yōu)化,在兩種自增方式中,如果沒有賦值操作,那么都會被優(yōu)化成一種方式,就像下面的兩個方法的代碼:

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

          最終執(zhí)行時的字節(jié)碼指令都是:

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

          可以看到,在上面的這種特定情況下,代碼經(jīng)過編譯器的優(yōu)化,保持了語義不變,并通過轉(zhuǎn)換語法的形式提高了代碼的運(yùn)行效率。所以再回到我們開頭的問題,就可以得出結(jié)論,在for循環(huán)中,通過jvm進(jìn)行編譯優(yōu)化后,不論是i++還是++i,最終執(zhí)行的方式都是++i,因此執(zhí)行效率是相同的。

          所以,以后再碰到這種半吊子的面試官,和你談for循環(huán)中i++++i的效率問題,自信點(diǎn),直接把答案甩在他的臉上,兩種方式效率一樣!

          本文代碼基于Java 1.8.0_261-b12 版本測試


          Java編譯相關(guān)文章推薦:

          String s="a"+"b"+"c",到底創(chuàng)建了幾個對象?

          2021-08-16

          面試官:說說什么是泛型的類型擦除?

          2021-08-24


          公眾號后臺回復(fù)

          "356"---領(lǐng)取100余本后端書籍

          "面試"---領(lǐng)取大廠面試資料

          "導(dǎo)圖"---領(lǐng)取24張Java后端學(xué)習(xí)筆記導(dǎo)圖

          "架構(gòu)"---領(lǐng)取29本java架構(gòu)師電子書籍

          "實(shí)戰(zhàn)"---領(lǐng)取springboot實(shí)戰(zhàn)項(xiàng)目


          關(guān)注公眾號

          有趣、深入、直接

          與你聊聊技術(shù)

          覺得有用,一鍵四連吧~

          瀏覽 21
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人久久久久亚洲AV无码裤子 | 伊人99re| 2024AV中文在线播放 | 堆女郎松果浴室自慰正在播放 | 波多野结衣av一区二区蜜桃观看 |