2014年2月18日 星期二

【Java】Make your object work better/fast...???

 That is my first English Blog post. And ,if you like to know, I never spend a day in school of Computer Science. So..... everything in this blog are amateur.
What is the different between those class???

If I use a loop to run a "new()" ... like new Test1(), or new Test2()... 100000 times, there are how much time they took to finish the loop.

Test1 :100 millisecond.
Test2:12000 millisecond.
Test3:34000 millisecond.

Obviously, more field the object has, more time it take to "new."

Is there more? Am I satisfied my own curiosity?

No! There is one more.


....... Interesting. They take the same time to finish the loop.

So the field make different, but method do nothing.



Wait!!!  There are more story about what happen when I use a infinity loop to test how many Test1/Test2/Test3 can a ArralyList<Test0> carry.......

Good night... it is 12 pm in Taiwan.

【Java】這樣設計物件有差喔!

差在哪裡?

我同樣用迴圈的方式放大其中的差異...在迴圈中執行「宣告動作」,──簡單來說,就是用類別 new 一個物件出來啦!

Test1執行的結果為100毫秒(大概的平均值)。

Test2執行的結果卻要12000毫秒。

Test3執行的結果更是增加到36000毫秒。

所以......愛護你/妳的程式、增加你/妳程式的效能,請從減少不必要的參數開始。

但萬一真的有必要,要使用「很多判斷式」.......把Test1/Test2/Test3改造成下列型式,然後參數規格都修改成一樣........


結果執行速度就差異不大...頂多1~2%,而且浮動性很高,極可能是「記憶體回收」或是其他干擾造成的。

重點在於:宣告階段影響程式效能的關鍵在「參數多寡」。



對!我聽到馬上有人質疑:反正一個物件宣告完後就擺在哪,何必計較宣告動作的效能呢?

今天假設是要做遊戲,比如某個關卡會有無限的「飛彈」、「小怪物」產生,「飛彈」不停被擊落、又不停射出,「小怪物」不停被打死、又不停被召喚........

當然可以把飛彈/怪物本身「被擊落與否」、「死亡與否」用純粹參數代替,(飛彈被擊落?重回原點等待發射,怪物死亡?重回原點等待召喚。)

如果採用這個做法,那確實沒有考量這點差異的必要。



但思考一下這個效能差異的由來......

雖然上面的附圖沒有截載,但關鍵在於記憶體。

記憶體配置?記憶體寫入?.......不管是哪一點,如果換個迴圈跑法,只是單純的做個ArrayList,然後一直塞物件進去、看記憶體何時爆掉........

Test3最快,Test2大約要Test3的三倍,Test1則是Test2的兩倍多一些......

還沒有機會測試使用了介面後的結果,但想必是相同的,因為使用介面根本不會有太多影響,介面(似乎)是類別的一部份,只會存在類別中,而不在物件存於記憶體的「標記/區段」上。

2014年2月11日 星期二

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?(3) 拿switch做比較

承續上一篇......我的「無聊神經」一旦開始運轉好像停不下來。

但是這次改用switch做比較。


我把這個迴圈用個函數包起來,a就是函數的傳入引數。

另外有一個函數在同樣的迴圈中寫入了「if...else...」,如下圖:




這兩個迴圈/函數執行的方式是......

run為switch,run1則是「if...else...」。為何要這樣做?run(1)和run(2)執行的結果不列入比較,原因看上一篇。剩下的.....做實驗當然要有充分的數據。

以下是結果...a是run函數輸出的,b則是run1......


先聲明:要使用switch或if來進行條件判斷,效能並不是優先考量......很多時候根本沒得選,因為switch只能判斷數值。(這算是「判斷」嗎?感覺跟分類沒兩樣。)

所以雖然效能明顯快了兩成,但其實只是作為一種比較而已。

2014年2月10日 星期一

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?(2)

上一篇,寫完後發現應該有個更客觀、更有準確的方法可以來測試,所以我先重新設計了作為基準/比對的「if...else...」,如下圖:




但跟上一篇不同的地方是我將它做成了一個函數...並且用以下的方法執行:


並且得到結果:

注意到了嗎...第1/2次的測試結果跟接下來的3/4/5/6差異過大。不管程式重複執行再多次也一樣。(這挺可怕的...雖然差異只是1~2%左右。)

不過這是插曲,接下來是我修改run()這個函數的內容的方法......


先聲明,用註解、或測底刪除程式,結果沒有明顯差異。測試結果如下圖.......




「if...else...」確實會明顯造成執行效能的差異。

這個測試法還有得搞.........(下一篇)

2014年2月7日 星期五

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?

「if」怎麼使用?

寫這個未免也太娘太小家子氣太學生口味了!(沒有瞧不起人的意思,感謝網路上無數的教學網站和教學文,但...你們還有需要加強的地方。)

參考一下我以前測試「效能」的方法,現在用來測下面這兩種「if」寫法在執行上會有多少效能差異......

IF1


IF2

上圖稱為IF1,下圖稱為IF2。因為我用個三層式的for迴圈來進行測試,所以會有i/j/k三個數值出現。

c1/c2的型態為long。看看他們被遞增的if條件式......是完全一樣的!也就是說如果c1被遞增、c2也會被遞增。唯一的差異就在於if...else的執行順序而已。

就理論上來說,「if」會被逐一執行,也就是說如果第一行if的條件吻合,則剩下的if都不會被執行,即使有條件更嚴謹、更精確、更穩和的。也就是說,理論上IF1執行起來會比較慢。

但另一個真正的問題是......要做if判斷,會需要耗掉多少資源。(沒辦法精準測量,只能取個概念。)

測試結果的彈性變化差異很大。



i/j/k的遞增極限都是1200,──k遞增到1200時會歸零,然後把j遞增1,j遞增到1200時也會歸零,然後把i遞增1......直到i也是1200,這個迴圈宣告結束。

limit1的值越小,IF1被執行到最後一行的機率就越高,理論上來說消耗的資源也越高。

當我把limit1定在400時,IF1執行完花了1769millisecond,IF2只花了836。

但當我把limit1定在800時,IF1執行完花了372millisecond,IF2只花了252。

當我再把limit1定在1000時,IF1執行完花了101millisecond,IF2只花了79。

最後但當我把limit1定在1200時,IF1執行完花了27millisecond,IF2只花了16。



這結果挺詭異的...我沒預想到,其實應該只要專注在小於800的數字就好。我擔心IF的條件太少會失去意義,但顯然我的IF條件太多...但又不夠多!(所以當limit1設在1200時,兩者的執行效率沒辦法達到一樣,因為我沒有精確的把所有i/j/k的狀況列入。)

可是結果肯定有差.......

如果寫了一長串的「if...else...」判斷式,然後又把常用的都擺在最後頭,程式的效能差異可以到達兩倍之多。(還沒有嘗試增加判斷式的難度會有何差別,那雖然不難.....但細節很多。)



這不單單只是效能測試而已!

程式碼光是簡潔還不夠,判斷式光是正確還不夠.......因為我們沒辦法預料這自己寫的判斷式會碰到什麼樣的資料/數據。我們沒辦法精準的保證「自己最前頭的判斷條件一定會被優先執行」。



所以會有使用函數並搭配return來控制函數執行的需要。不過這是改天再寫的了。

(但如果是我,我會用物件的多型性來取代這一長串判斷式。這有機會再寫的了。



點這是看續篇.......(我怎麼這麼無聊,連這種東西都寫續篇。)

2014年1月22日 星期三

[Java/多型性]多型性和強制轉換類別... (試過才知道)

真的稍微搞懂Java物件導向的人,都知道多型性是啥。

「參數型別宣告為父類別,則可以存入所有子類別。」.......講得很潦草,但大概是那樣。

可是這個特性具體來說會造成什麼影響?

子類別A強制轉型為子類別B時,會發生什麼事?

要測試多型性,自然要寫物件。以下有A(只是測試,就別花心思去取名了,)和A1/A2。

接著是初步測試用的程式......


可能有些人會理解、可能有些人不會,甚至有些人直接笑我蠢,但我真的想知道參數和函數對型別強制轉換的反應有何差異....

如果A2的test()內的傳入參數型別指定為A,因為多型性的理由,不用考慮一定都會順利執行。

但如果指定為A2,然後用A或A1強制轉型呢?



父類別似乎無法順利強制轉型為子類別,...不僅僅只是會損失資料而已,重新叫出了error訊息,訊息告訴我「無法如此轉型」。

顯然類別之間的型別轉換跟基礎數值的型別轉換不同,不是只會損失資料而已,而是根本上就限制了使用方式。

所以......所謂的「多型性」並不是種物件導向的特性,而是種人為操作/設計出來的規則。

不知道有沒有人能驗證這樣的解釋?



這次的實驗感覺很基礎很簡單很沒意義,但我不想知道怎樣可以成功,不想知道怎樣可以搞出「讓世人讚嘆的多型性應用」,但我對「怎樣操作多型性會失敗」很有興趣。就.......「浪費時間」吧!

2014年1月17日 星期五

[Java/Android]選取Spinner的內容同時改變Button的功能 (我會怎麼用純物件的方式寫這個功能)

(接續上一篇)還是這個功能/介面,資料處理方式可能是「各種加密方法」或「將資料傳向特定網站或資料庫」。

用「進化」的觀點來看,結構化程式設計技巧確實比物件化程式設計技巧更基礎。(前者在60~70年代出現並成熟、被發揚光大,後者雖然同時期出現,但Java卻是90年代的東西了。)

不用物件導向技巧還是可以在物件導向語言裡寫出可以運作良好的程式,只是...「維護」和「擴充」(特別是要由第三者來維護跟擴充)就很讓人「蛋疼」了。(女工程式請不要介意...)




我會先設計一個這樣的介面Interface(不是UI介面)。

看名稱應該就知道,這是擴充後要給Spinner/下拉彈出式選單作為內容的介面。

我擴充這個介面產生一個新類別,並在getText()中設定「return "功能一"」,如果用這個類別宣告實作物件,並將物件塞給Spinner的SpinnerAdapter,則選單中就會有「功能一」這個選項。以此類推...要多少有多少。(中間有一些設定SpinnerAdapter的細節,如果不知道自己推敲應變,那我這段描述會變得很怪。或者你知道使用其他Adapter來取代......)

然後再設計一個OnItemSelectedListener,──給Spinner使用的。這個OnItemSelectedListener具有「偵測」使用者選取了哪個Spinner選單內容的能力。(看到第22行的程式碼,那是個函數,裡頭有個「int position」,這就是「使用者選擇的參數」,──或者說是「選單中的第幾個選項」。)

Button b1就是「送出」鈕。藉著將這個Listener設定給Spinner,程式會即時、動態(使用者每選擇一次選單內容)的變換這個按鈕的OnClickListener。(list就是用來「裝」選單內容的地方/物件。)

(上面這個程式範例有嚴重的錯誤就是「它是OnItemClickListener」。但Spinner雖然保有這個屬性,但卻無法使用,執行Spinner的setOnItemClickListener......還沒成功過。幸運的是,除了名稱不一樣以外,它和OnItemSelectedListener結構與傳入參數都一模一樣。)



如下圖,這是Activity,可以看到我一共產生了三個選項,分別是「Test1」、「Test2」、「Test3」。(恕我省略HsuSpinnerAdapter的內容。因為就是個繼承自SpinnerAdapter的類別)



這支程式只會很可笑的把「Test1」、「Test2」、「Test3」作成小框框內的文字顯示在畫面上而已。但確實是「三個不同的功能」。



如果想要,可以自己設計獨特的SpinnerItem,然後放進list中,未來要擴充功能就會變得很簡單。

「難道不用物件導向技巧會變得很難寫嗎?」

這樣假設吧......今天有個「Test1.5」要放進「Test1」和「Test2」之間,那可能只要在ArrayList<String>的 「Test1」和「Test2」之間放進「Test1.5」,就會出現這個選項。

問題是「送出」按鈕的OnCLickLitener中,要將所有判斷式的position值重新修改,......其實也不難,但是如果又出現了「Test1.4」、「Test1.3」、「Test2.1」.......無限增加中,只要這個程式還活著、專案管理和企劃人員腦筋動個不停,這種事情就會發生。

當OnClickListener的程式碼長達八九百行、甚至一千五百行時,不停重複修改一支Class就不是讓人很愉快的事情了。

所以為何不在程式剛開始、專案剛起頭時,想好一個將來方便擴充的機制呢?

(文章寫到這裡,我又想到一個修改SpinnerItem的方法,將來就算要直接新增一個選單,──也就是一共會有兩個選單──也可以輕鬆擴充、修改的設計,......物件導向就是這麼迷人。)



上面的那個問題好像有點廢話,但問題是「有些工程師會認為像我這樣寫程式才是廢話。」

因為「物件架構」不是一翻兩瞪眼的東西,如果架構不可行,理論上有時後看不出來,要有具體程式碼才能驗證,而且驗證一次不夠,可能要兩三次。

第一次驗證失敗後,已經有一個功能和三千行程式碼,第二個功能進行設計完成時,要進行第二次驗證,結果又失敗,這時已經有六千行程式碼,結果這六千行幾乎都要打掉重寫,重第一個功能開始驗證......重複這樣的過程,直到確定架構可以重複運作在...「所有功能上」。

可能五六個星期已經過去了。

但是......直接抓起規格書,開始用土法煉鋼的「一個功能就一個物件/Activity」這樣寫,可能兩個禮拜就把初步規格書上所有的功能都完成了。

當這些工程師可以不斷一次又一次超前進度、快速精準完成專案時,「物件導向」就變成了「Java語言不方便的特性」而已。去精熟物件架構、去專研怎樣設計物件架構、累積設計物件架構的經驗.......只是一場笑話罷了。




這就是JAVA設計師的絕境與哀愁啊!物件導向其實好學!只要認真寫教學文、教學範例。甚至可以用Library重新包裝所有JAVA基礎程式指令,例如更精簡、更快速的Socket建立方式。寫JAVA?哪那麼難?

但是一堆人把JAVA當成單純的結構式語言、當成C/C++工程師找工作時的代替品,認為「要精通很多指令」(或說是「同一個指令要有很多次使用的經驗」)、「要能閱讀並掌握大量程式碼」、「要能快速、用直線思考的方式實現規格書的需求」......才是工程師之道。