这篇主要讲一些平时写代码时优化的小技巧。虽然看上去都是一些很小的细节,可是聚沙成塔,量变到必定程度也会发生质变,积累的性能提高效果仍是不可忽视的。平时碰到这些问题时必定要多留心,提高本身编码水平的同时也能增强代码的健壮性。算法
这里简单介绍一些经常使用的数据类型的选择与使用场景。后端
咱们平时在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的提示,能够改进不少代码中不规范的地方,提升效率。具体使用就不展开讲了,网上一搜一大把
暂时先写这么多,之后有补充再更新