Java性能优化

9/6/2015来源:Java教程人气:3747

java性能优化作者:禅楼望月(http://www.cnblogs.com/yaoyinglong)

注:里面的测试结果会因电脑配置的不同而有所差异!!!

1. 为一些集合定义初始化大小

List、Set、Map都会有有一个默认的初始化大小,但是这个值往往不够我们使用,这时候JVM便会申请更大的一块内存给集合,然后将原集合中的数据复制过来,最后原集合等待被作为垃圾而回收。可见扩容是一件比较费事的事情,所以最好能准确的估计你所需要的最佳大小。

[+]view code

publicclass PerformanceOptimization {

publicstaticvoid main(String[] args) { long start=new Date().getTime(); List<String> a=new ArrayList<String>(); for(int i=0; i<1000000; i++){ a.add("a"); } long end=new Date().getTime(); System.out.PRintln(end-start);//47 } }

优化后:

[+]view code

publicclass PerformanceOptimization {

publicstaticvoid main(String[] args) { long start=new Date().getTime(); List<String> a=new ArrayList<String>(1000000); for(int i=0; i<1000000; i++){ a.add("a"); } long end=new Date().getTime(); System.out.println(end-start);//32 } }

这样积少成多,效果还是挺可观的。

2. 复制数组的时候使用System.arraycopy来代替循环copy,它的效率更高更简洁。 [+]view code

publicclass PerformanceOptimization {

publicstaticvoid main(String[] args) { int[] array1 = newint [100]; for (int i = 0; i < array1.length; i++) { array1 [i] = i; } int[] array2 = newint [100]; System.arraycopy(array1, 0, array2, 0, array1.length); }} 3. 避免不需要的instanceof操作

如果左边的对象的静态类型等于右边的,instanceof表达式返回永远为true。

[+]view code

publicclass Dog extends Uiso {

void method (Dog dog, Uiso u) { Dog d = dog; if (d instanceof Uiso) // always true. System.out.println("dog is a uiso"); Uiso uiso = u; if (uiso instanceof Object) // always true. System.out.println("uiso is an object"); }} 4. 避免不需要的造型操作

所有的类都是直接或者间接继承自object。同样,所有的子类也都隐含的“等于”其父类。那么,由子类造型至父类的操作就是不必要的了。如:

class dog extends unc

dog dog = new dog ();

unc animal = (unc)dog; // not necessary

object o = (object)dog; // not necessary

5. 如果只是查找单个字符的话,用charAt()代替startsWith [+]view code

publicclass PerformanceOptimization {

publicvoid method(String s) { if (s.startsWith("a")) { // violation // ... } } }

将'startsWith' 替换成'charAt()'

[+]view code

publicclass PerformanceOptimization {

publicvoid method(String s) { if ('a'==s.charAt(0) ) { // ... } } } 6. 使用移位操作来代替'a / b'和'a * b'操作

"/"和"*"都是一个很“昂贵”的操作,使用移位操作将会更快更有效。

[+]view code

publicclass PerformanceOptimization {

publicstaticfinalintnum = 16; publicvoid calculate(int a) { int div = a / 4; // should be replaced with "a >> 2". int div2 = a / 8; // should be replaced with "a >> 3". int temp = a / 3; // 不能转换成位移操作 }

}

publicclass PerformanceOptimization { publicstaticfinalintnum = 16; publicvoid calculate(int a) { int mul = a * 4; // should be replaced with "a << 2". int mul2 = 8 * a; // should be replaced with "a << 3". inttemp = a * 3; // 不能转换成位移操作 } }

注:除非是在一个非常大的循环内,性能非常重要,而且你很清楚你自己在做什么,方可使用这种方法。否则提高性能所带来的程序可读性的降低将是不合算的。

7. 不要在循环中调用synchronized(同步)方法

方法的同步需要消耗相当大的资源,在一个循环中调用它绝对不是一个好主意。

[+]view code

publicclass PerformanceOptimization {

publicsynchronizedvoid method (Object o) { } privatevoid test () { for (int i = 0; i < vector.size(); i++) { method (vector.elementAt(i)); // violation } } private Vector vector = new Vector (5, 5); }

更正:不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式: [+]view code

publicclass PerformanceOptimization {

publicvoid method (Object o) {} privatevoid test () { synchronized(this){//在一个同步块中执行非同步方法 for (int i = 0; i < vector.size(); i++) { method (vector.elementAt(i)); } } } private Vector vector = new Vector (5, 5); } 8. 将try/catch块移出循环

把try/catch块放入循环体内,会极大的影响性能,如果编译jit被关闭或者你所使用的是一个不带jit的jvm,性能会将下降21%之多!

[+]view code

publicclass PerformanceOptimization {

void method (FileInputStream fis) { for (int i = 0; i < 100; i++) { try { // violation _sum += fis.read(); } catch (Exception e) {} } } privateint_sum;} 9. 对于boolean值,避免不必要的等式判断

将一个boolean值与一个true比较是一个恒等操作(直接返回该boolean变量的值). 移走对于boolean的不必要操作至少会带来2个好处:

1)代码执行的更快 (生成的字节码少了5个字节);2)代码也会更加干净。 [+]view code

publicclass PerformanceOptimization {

boolean method (String string) { // return string.endsWith ("a") == true; // violation return string.endsWith ("a"); } } 10. 对于常量字符串,用'string' 代替 'stringbuffer'

常量字符串并不需要动态改变长度。把stringbuffer换成string,如果确定这个string不会再变的话,这将会减少运行开销提高性能。

11. 用'StringTokenizer' 代替 'indexof()' 和'substring()'

字符串的分析在很多应用中都是常见的。使用substring()来分析字符串容易导致java.lang.StringIndexOutOfBoundsException。而使用StringTokenizer类来分析字符串则会容易一些,效率也会高一些。

12. 使用三元运算表达式替代"if (cond) XXX; else XXX;" 结构。 13. 尽量不要在循环体中实例化变量

在循环体中实例化临时变量将会增加内存消耗

14. 尽可能的使用栈变量

如果一个变量需要经常访问,那么你就需要考虑这个变量的作用域了。static? local?还是实例变量?访问静态变量和实例变量将会比访问局部变量跑的路径要长的多。

[+]view code

publicclass PerformanceOptimization {

void getsum (int[] values) { for (int i=0; i < values.length; i++) { _sum += values[i]; // violation. } } void getsum2 (int[] values) { for (int i=0; i < values.length; i++) { _staticsum += values[i]; // violation. } } privateint_sum; privatestaticint_staticsum; }

更正:如果可能,请使用局部变量作为你经常访问的变量。可以按下面的方法来修改getsum()方法: [+]view code

void getsum (int[] values) {

int sum = _sum; // temporary local variable. for (int i=0; i < values.length; i++) { sum += values[i]; } _sum = sum; } 15. 与一个接口 进行instanceof操作

基于接口的设计通常是件好事,因为它允许有不同的实现,而又保持灵活。只要可能,对一个对象进行instanceof操作,以判断它是否某一接口要比是否某一个类要快。

16. 尽量减少对变量的重复计算

比如

for(int i=0;i<list.size();i++)

应修改为

for(int i=0,len=list.size();i<len;i++) 17. 考虑使用静态方法

如果你没有必要去访问对象的外部,那么就使你的方法成为静态方法。她会被更快地调用,因为她不需要一个虚拟函数导向表。这同时也是一个很好的实践,因为她告诉你如何区分方法的性质,调用这个方法不会改变对象的状态。