代碼的優(yōu)化 性能的提升 - JAVA

這篇將作為我這一段時(shí)間以來(lái)結(jié)合項(xiàng)目、學(xué)習(xí)對(duì)于自我提升的過(guò)程中的一個(gè)技術(shù)性的經(jīng)驗(yàn)總結(jié)??挣饥。

現(xiàn)在大多數(shù)公司招人看中項(xiàng)目經(jīng)驗(yàn)、工作經(jīng)驗(yàn)扔枫,我也承認(rèn)這些也確實(shí)非常重要。但是我時(shí)常會(huì)想短荐,是否有一條捷徑,可以讓我們更快的強(qiáng)大起來(lái)忍宋、更快的走向成功?我也不確定讶踪,我也在摸索著前行...但我相信,站在巨人們的肩旁上乳讥,借鑒他們探索出的東西,加上自己的那一份努力云石,至少可以讓自己少走一些彎路。

為什么會(huì)寫(xiě)這篇程序優(yōu)化的總結(jié)汹忠,主要是面向如今這個(gè)大數(shù)據(jù)驅(qū)動(dòng)的時(shí)代。如果沒(méi)有大批量的數(shù)據(jù)讓你去處理宽菜、如果沒(méi)有大量業(yè)務(wù)需要你去書(shū)寫(xiě),那么有必要去在乎那零點(diǎn)幾毫秒的優(yōu)化或是復(fù)用那些代碼嗎铅乡?

1. 選用恰當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)

去重操作是我們?nèi)粘?huì)遇到的一個(gè)問(wèn)題。我無(wú)數(shù)次遇到去重的問(wèn)題的時(shí)候阵幸,可能大家想的都一樣: 臥槽,也太簡(jiǎn)單了挚赊,給我兩秒鐘...然后做了如下操作:

ArrayList<String> arr = new ArrayList<String>(Arrays.asList(......)); //待去重?cái)?shù)組 

ArrayList<String> arrTemp = new ArrayList<String>();
for(String str: arr) {
  if (!arrTemp.contains(str)) {
    arrTemp.add(str);
    // 處理操作
  }
}

這是正常人的思路吧,使用一個(gè)臨時(shí)數(shù)組來(lái)存放新的元素荠割,然后便利的時(shí)候判斷是否包含。嗯...功能實(shí)現(xiàn)了,但是這個(gè)待遍歷的數(shù)組變成了幾十萬(wàn)條數(shù)據(jù)...然后我按下了 run 跑程序并等待程序的結(jié)束纽帖,然后我在程序finish 之前陷入了長(zhǎng)時(shí)間的沉思......然后到處找呀找举反,查閱資料和博客,最后發(fā)現(xiàn)了一種效率非常贊的數(shù)據(jù)結(jié)構(gòu) - HashSet火鼻, 我非常激動(dòng),MD太辛苦了找了辣磨久終于找到好的辦法了融撞,這個(gè)時(shí)候之前的程序運(yùn)行結(jié)束了......

先想想為什么這種數(shù)組的方式會(huì)很慢。在我們每一次循環(huán)中粗蔚,我們都會(huì)使用contains()去遍歷整個(gè)arrTemp,隨著數(shù)組越來(lái)越長(zhǎng)致扯,速度會(huì)越來(lái)越慢。接下來(lái)看看 HashSet 的存儲(chǔ)結(jié)構(gòu)示意圖:

HashSet存儲(chǔ)結(jié)構(gòu).jpg

??在HashSet中抖僵,每一個(gè)key都對(duì)應(yīng)著一個(gè)hashcode缘揪,因此通過(guò)計(jì)算可以先找到是在數(shù)組中 0~15 的哪一個(gè)位置,然后再去尋找這條鏈上有沒(méi)有這個(gè)對(duì)應(yīng)的key值找筝。這么一來(lái)在速率上明顯快了不上。

ArrayList<String> arr = new ArrayList<String>(Arrays.asList(......)); //待去重?cái)?shù)組 

HashSet set = new HashSet();

for (String str : arr) {
  if (set.add(str)) {
        // 沒(méi)有重復(fù)元素
  } else {
       // 存在重復(fù)元素...
  }
}

換了一種數(shù)據(jù)結(jié)構(gòu)袖裕,在程序的執(zhí)行效率上有了很大的改觀。所以說(shuō)沐祷,選擇一種好的數(shù)據(jù)結(jié)構(gòu)是很重要的~

2. 養(yǎng)成復(fù)用對(duì)象的好習(xí)慣

在程序的運(yùn)行過(guò)程中攒岛,JVM堆上存放著活對(duì)象和程序無(wú)法再次引用的垃圾對(duì)象,當(dāng)堆空間的空閑部分低于某個(gè)比例或者無(wú)法滿足為新對(duì)象分配空間時(shí)灾锯,JVM都會(huì)觸發(fā)一次垃圾收集(Garbage Collect,GC),觸發(fā)gc時(shí)吵聪,JVM會(huì)暫停所有的用戶線程。gc觸發(fā)太過(guò)頻繁或是時(shí)間過(guò)長(zhǎng)帽蝶,都會(huì)導(dǎo)致程序性能大大下降。
所以說(shuō)励稳,遇到此類問(wèn)題我們應(yīng)該怎么辦呢囱井? 我們需要盡量去重用對(duì)象,以減少gc的觸發(fā)幾率庞呕。比如說(shuō)像這樣:

String str = "";
for(...) {
  str = ....;
  str ......
}

順便說(shuō)一下,不只是對(duì)象的創(chuàng)建住练,循環(huán)里面最好別放一些沒(méi)必要的操作,特別是耗時(shí)的操作澎羞。

3. 提高字符串拼接的效率

字符串的拼接是我們寫(xiě)程序的時(shí)候最最常見(jiàn)的情況了,我相信大家肯定都遇到過(guò)顺呕,并且以后也會(huì)繼續(xù)和它打交道。對(duì)于文件IO讀寫(xiě)而言括饶,大家一定對(duì)于這句話并不陌生:

str += stringBuffer.readLine() + "\n";

我們?cè)谧x取文件的我時(shí)候,經(jīng)常會(huì)按行來(lái)讀取启盛,然后將其內(nèi)容進(jìn)行依次追加,最后將內(nèi)容結(jié)果寫(xiě)出僵闯。

如果在循環(huán)次數(shù)不多的情況下藤滥,并且對(duì)效率的要求不高的話鳖粟,為了圖方便這么些沒(méi)問(wèn)題拙绊。但是泳秀,如果循環(huán)次數(shù)非常多的話榄攀,這種方式做字符串的拼接效率實(shí)在太低了。這里有一種更好的方式:使用 StringBuilder 檩赢,我們來(lái)看一個(gè)效率的對(duì)比結(jié)果吧。

首先我們假設(shè)有一個(gè)100000次的循環(huán)漠畜,每一次里面都會(huì)將內(nèi)容"aaa123"追加到結(jié)果中坞靶。

   long start = System.currentTimeMillis();
        int size = 100000;
        String result = "";
        for (int i = 0; i < size; i++) {
            result += "aaa123";
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start + "ms");
string追加.png

然后再來(lái)看一下改成StringBuilder之后的代碼和運(yùn)行結(jié)果:

  long start = System.currentTimeMillis();
        int size = 100000;
        String result = "";
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            builder.append("aaa123");
        }
        result = builder.toString();
        long end = System.currentTimeMillis();
        System.out.println(end - start + "ms");
StringBuilder追加.png

這效率簡(jiǎn)直高了幾個(gè)數(shù)量級(jí)了...為什么StringBuilder可以做到在這樣多的循環(huán)次下達(dá)到這么高的效率呢彰阴?我大致解釋下...

對(duì)于使用'' + ''號(hào)來(lái)追加的方式而言,String對(duì)象在賦值以后是沒(méi)法改變的尿这,所以這種使用'' + ''號(hào)的追加原理其實(shí)每一次都重新創(chuàng)建了一個(gè)String 對(duì)象,然后對(duì)其賦值射众。這樣不斷新建對(duì)象,就像之前我們所講的那樣叨橱,會(huì)頻繁觸發(fā)gc,而且會(huì)隨著內(nèi)容越來(lái)越多使得創(chuàng)建String對(duì)象的速度越來(lái)越慢愉舔。

然而對(duì)于StringBuilderappend追加方式,是將string 轉(zhuǎn)換為一個(gè)char[] 數(shù)組伙菜,然后通過(guò)擴(kuò)展數(shù)組的方式將追加內(nèi)容放入原始數(shù)組中轩缤。

4. for循環(huán)的正確使用姿勢(shì)

對(duì)于for循環(huán)的使用,我的建議是:

  • 遍歷數(shù)組使用基于索引的循環(huán)方式
  • 遍歷集合使用基于迭代器的循環(huán)方式

具體的怎么去使用贩绕,我在這里就不做過(guò)多說(shuō)明了火的。
主要講講怎么進(jìn)行循環(huán)的優(yōu)化吧。

(1) ''外小內(nèi)大''原則
 for (int i = 0; i < 1000000 ; i++ ) {
            for (int j = 0; j < 1000 ; j++ ) {
                for (int k = 0 ; k < 10; k++) {

                }
            }
        }
循環(huán)1.png

這是一個(gè)典型的循環(huán)嵌套的結(jié)構(gòu)淑倾,但是呢for循環(huán)應(yīng)該是要滿足一個(gè)''外小內(nèi)大''的模式馏鹤。這是我們改良后的代碼:

 for (int i = 0; i < 10 ; i++ ) {
            for (int j = 0; j < 1000 ; j++ ) {
                for (int k = 0 ; k < 1000000; k++) {

                }
            }
        }
循環(huán)2.png

結(jié)果很明顯,運(yùn)行效率上面快了不少踊淳。但是這還不是最完美的做法~

        int i,j,k;
        for (i = 0; i < 10 ; i++ ) {
            for (j = 0; j < 1000 ; j++ ) {
                for (k = 0 ; k < 1000000; k++) {

                }
            }
        }

嗯假瞬,這樣差不多就比較完美了??

(2)避免沒(méi)必要的操作

還是先來(lái)看一段原始代碼:

for(int i = 0; i < list.size(); i++) {
     i = i * a * b;
}

這段代碼有兩個(gè)問(wèn)題:第一個(gè)問(wèn)題是每次循環(huán)都會(huì)進(jìn)行一次 list.size()的計(jì)算陕靠,第二個(gè)問(wèn)題是每次都會(huì)進(jìn)行 a * b的計(jì)算。這些都是只需要進(jìn)行一次的操作脱茉,沒(méi)必要每次都進(jìn)行剪芥。

優(yōu)化后的代碼如下:

int c = a * b;
int len = list.size();
for(int i = 0; i < len; i++) {
    i = i * c;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市琴许,隨后出現(xiàn)的幾起案子税肪,更是在濱河造成了極大的恐慌,老刑警劉巖榜田,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異净捅,居然都是意外死亡辩块,警方通過(guò)查閱死者的電腦和手機(jī)废亭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)豆村,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掌动,“玉大人,你說(shuō)我怎么就攤上這事坏匪∈首遥” “怎么了凭迹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嗅绸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我猛拴,道長(zhǎng)愉昆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任焊切,我火速辦了婚禮专肪,結(jié)果婚禮上嚎尤,老公的妹妹穿的比我還像新娘抖格。我一直安慰自己雹拄,他們只是感情好滓玖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布势篡。 她就那樣靜靜地躺著禁悠,像睡著了一般碍侦。 火紅的嫁衣襯著肌膚如雪隶糕。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音再登,去河邊找鬼晾剖。 笑死钞瀑,一個(gè)胖子當(dāng)著我的面吹牛雕什,可吹牛的內(nèi)容都是我干的显晶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唯笙!你這毒婦竟也來(lái)了崩掘?” 一聲冷哼從身側(cè)響起苞慢,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挽放,失蹤者是張志新(化名)和其女友劉穎辑畦,沒(méi)想到半個(gè)月后纯出,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年分扎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畏吓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宏悦,靈堂內(nèi)的尸體忽然破棺而出饼煞,到底是詐尸還是另有隱情,我是刑警寧澤息堂,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布荣堰,位于F島的核電站振坚,受9級(jí)特大地震影響屡拨,放射性物質(zhì)發(fā)生泄漏褥实。R本人自食惡果不足惜损离,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一僻澎、第九天 我趴在偏房一處隱蔽的房頂上張望窟勃。 院中可真熱鬧秉氧,春花似錦蜒秤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赖瞒。三九已至蚤假,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抡爹,已是汗流浹背冬竟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工泵殴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笑诅,地道東北人吆你。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓妇多,卻偏偏與公主長(zhǎng)得像者祖,于是被迫代替她去往敵國(guó)和親七问。 傳聞我的和親對(duì)象是個(gè)殘疾皇子械巡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法芦鳍,類相關(guān)的語(yǔ)法柠衅,內(nèi)部類的語(yǔ)法菲宴,繼承相關(guān)的語(yǔ)法喝峦,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,631評(píng)論 18 399
  • 集合框架: 1)特點(diǎn):存儲(chǔ)對(duì)象;長(zhǎng)度可變谈喳;存儲(chǔ)對(duì)象的類型可不同2)Collection(1)List:有序的婿禽;元素...
    Demo_Yang閱讀 1,260評(píng)論 0 4
  • 在 Java 中,內(nèi)存的分配是由程序完成的吆录,而內(nèi)存的釋放則是由 Garbage Collecation(GC) 完...
    Shawn_Dut閱讀 5,889評(píng)論 3 28
  • CALayer的屬性mask巨坊,mask也是一個(gè)layer趾撵,這邊稱它為maskLayer暂题。介紹中描述 An opti...
    linatan閱讀 1,208評(píng)論 0 2
  • OXM簡(jiǎn)介 我們都知道對(duì)象關(guān)系映射(ORM),用來(lái)將Java對(duì)象和關(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行映射剿涮。Spring也提供了...
    樂(lè)百川閱讀 4,391評(píng)論 1 3