這篇主要講一些平時寫代碼時優化的小技巧。雖然看上去都是一些很小的細節,可是聚沙成塔,量變到必定程度也會發生質變,積累的性能提高效果仍是不可忽視的。平時碰到這些問題時必定要多留心,提高本身編碼水平的同時也能增強代碼的健壯性。算法
這裏簡單介紹一些經常使用的數據類型的選擇與使用場景。後端
咱們平時在Java中作字符串鏈接的時候,下意識的選擇都是使用 +
來鏈接。這個過程其實會新生成一個 StringBuilder
對象,而後將 +
左右的數據經過 append()
方法拼接起來,本質上就是**使用StringBuilder
對象進行字符串鏈接。**因此在拼接頻繁的場景(好比循環)中,若是使用 +
就至關於每次都會新建一個StringBuilder
對象。而咱們知道,頻繁新建對象是很消耗性能的,並且在循環中也容易發生內存抖動。數組
結論:在單條語句中使用
+
直接拼接沒有效率問題;拼接頻繁的場景請使用StringBuilder
。安全
另外,還有個StringBuffer
用法與 StringBuilder
幾乎同樣,只是前者是線程安全的然後者非線程安全,這裏就不展開說了。數據結構
其實原理很簡單,不一樣的數據類型,佔用的內存空間不同。好比int
只佔了 4 個字節,而long
佔用了 8 個字節,很明顯地處理起來 int
要快於 long
。可是,在具體的工做中,由於受到各類因素的制約,好比第三方庫或者後端接口返回的數據每每不肯定,加之性能上影響其實並非很大,因此爲了保證數據正確性,對這塊的約束通常並非太嚴格。app
結論:在本身可以預見的場景中儘可能使用
int
short
甚至byte
來代替long
;float
之於double
同理。工具
雖然Java中針對每種基本數據類型,都有包裝類來對應,並且對應的包裝類都提供了自動裝箱和自動拆箱的能力,可是包裝類也不能濫用。由於在給包裝類賦值的時候,實際是經過valueOf
方法去新建了一個對象,而基本數據類型的賦值倒是直接在棧空間內完成的,效率就快了不少。 可是不是包裝類就不要用了?也不是。包裝類做爲對象,提供了不少相關的操做方法,方便操做;另外一方面,包裝類能夠很方便地區分賦值與未賦值的狀況,而基本數據類型沒法區分。這是在調用後端接口時常常會碰到的狀況,因此不能一律而論。性能
結論:儘可能使用基本數據類型來賦值以提升效率;可是在區分賦值和未賦值的場景時請使用包裝類優化
修飾符分爲訪問控制修飾符和非訪問控制修飾符。訪問控制修飾符就是咱們平時見到的private
protected
public
等對代碼訪問權限進行控制的符號,這裏就不展開講了;這裏主要談談非訪問控制修飾符,經常使用的就是static
final
這兩個(volatile
也先略過不提)。ui
static
靜態修飾符 使用了該修飾符,則表示該變量隨着當前類的生命週期共存亡,而且該變量會被存到方法區(JVM中的一塊固定區域)於是可被全部對象共享,即全部實例均可以經過類名來使用該變量。
final
最終修飾符 使用了該修飾符,則表示此變量的生存期內,值是不可能改變的。常量若是使用final
來修飾的話,讀取效率較高。
結論:很明顯,若是是常量,那麼使用
static final
來修飾是能夠提升效率的。
在選擇數據結構的時候,咱們有時會選擇用得最順手的那個。卻不知,不一樣的數據結構,執行效率千差萬別。正確選擇更好更高效的數據結構是代碼優化必須作到的。
這兩個數據結構都是繼承於AbstractList
並實現了List
接口,不一樣之處在於ArrayList
底層數據結構使用的是數組,而 LinkedList
底層數據結構使用的是鏈表。所以在什麼場合使用就很明顯了。
結論:**隨機查找與修改元素,使用
ArrayList
效率更高;對於新增和刪除元素較多的場景,則最好使用LinkedList
。**這是由數組和鏈表的性質決定的
這三個數據結構都是基於Hash算法的數據結構,可是底層實現都不同。HashMap
和 HashTable
都是實現了Map
接口,可是前者繼承AbstractMap
且非線程安全,後者繼承的是Dictionary
是線程安全的;而HashSet
實現的則是Set
接口,並且效率相對於HashMap
要低一些
結論:這幾個實現Hash算法的數據結構,弄清楚了他們之間的區別和聯繫,就能明白使用場景了
具體來講,SparseArray
是 Android 官方推薦的一種用來代替HashMap
的數據結構,更加節省內存。可是在查找效率上,SparseArray
因爲查找核心算法是二分查找,比HashMap
稍慢一點,可是相對來講效率損失並非很大。
結論:在須要節省內存空間,或者對增刪改查效率要求不是很是苛刻的場景,優先使用
SparseArray
一樣的,Parcelabel
也是 Android 官方推薦的一種序列化/反序列化代碼的數據結構,比 Serializable
更加高效。由於在讀寫數據的時候,Parcelabel
是直接在內存中讀寫數據,而 Serializable
是經過 I/O 方式將數據讀寫在磁盤上,顯然前者讀寫速度更快
結論:優先使用
Parcelabel
序列化/反序列化,但一些場景中仍是須要使用Serializable
在某些特定場景中,使用移位運算比直接乘除效率要高不少,這是由計算機底層特性決定的。好比 i / 2
就能夠表示爲 i >> 1
結論:培養習慣,看到這種場景要下意識想到使用位運算。但這樣會致使代碼可讀性變差,因此請清楚註釋
由於生成一個新對象在 Java 虛擬機中是一個比較耗時耗性能的操做,並且在用完這個新對象以後,系統還要對這些生成的對象進行GC,這又是一筆性能開銷。因此,頻繁生成過多的對象對性能會形成很大影響。
結論:不要建立非必須的對象,能複用儘可能複用。尤爲是要避免在循環體內新建對象,避免內存抖動
由於臨時變量都保存在棧裏,讀取速度比堆中要快;另外,棧中的變量在方法結束時就銷燬了,不須要進行額外的GC。
結論:在不須要的地方儘可能不要使用全局變量
請在類的內部直接訪問私有變量,而不是經過 get
set
方法來訪問,能夠提升代碼運行效率。get
set
方法是提供給外部調用的
根據Android官方文檔,在沒有JIT(Just In Time)編譯器時,直接訪問變量的速度是調用Getter方法的3倍;在JIT編譯時,直接訪問變量的速度是調用Getter方法的7倍
循環體每每是影響效率的關鍵環節,一些影響效率的因素,原理其實很簡單,因此直接說結論。
- 儘可能避免在循環體中新建對象以減小內存抖動
- 不要把
try ... catch
語句寫在循環體內部
Lint是個很是有用的工具,通常根據Lint的提示,能夠改進不少代碼中不規範的地方,提升效率。具體使用就不展開講了,網上一搜一大把
暫時先寫這麼多,之後有補充再更新