本文共 8307 字,大约阅读时间需要 27 分钟。
现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升,在开发中注意这些,更多的是可以保持一种性能优先的意识。
一 条件控制语句中的优化
1.在循环中应该避免使用复杂的表达式。
在循环中,循环条件会被反复计算,应该避免把一些计算放在循环进行的部分中,程序将会运行的更快。
比如:for(int i=0;i
二 更好的使用变量
1.合理使用final修饰符注意fina修饰变量或者方法时的用法不同。很多时候使用"static final 变量"格式是很好的选择。
2.尽量避免不必要的创建,同时避免使用随意使用静态变量GC通常是不会回收静态变量对象所占有的内存,所以要合理的使用静态区域代码。
3.使用基本数据类型代替对象 Java中,String对象主要由3部分组成:char数组、偏移量和String的长度。String str = "hello"; //创建一个“hello”字符串,而且JVM的字符缓存池还会缓存这个字符串;String str = new String("hello")// 此时程序除创建字符串外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组依次存放了h,e,l,l,o
二 更高效的使用字符串
1.对于常量字符串,用'string' 代替 'stringbuffer',但是在需要String对象做累加操作的场合,使用StringBuilder或者StringBuffer;另外,在单线程或者无需考虑线程安全的情况下,使用性能相对较好的StringBuiler(单人盖房,多人缓冲)。
2.使用更好的办法分割字符串
《Java程序性能优化》一书中指出,split()方法支持正则表达式,功能强大,但是效率最差;StringTokenzer性能优于split()方法。如果可以自己实现分割算法,性能可以做到最优,但是考虑到可读性可维护性等,可以使用StringTokenizer。 在代码中验证一下:StringBuilder sb=new StringBuilder();for(int i=0;i<100000;i++){sb.append(i);sb.append(",");}String str=sb.toString();Long time1=System.currentTimeMillis();String[] split=str.split(",");Long time2=System.currentTimeMillis();System.out.println(time2-time1);Long time3=System.currentTimeMillis();StringTokenizer st=new StringTokenizer(str,",");String[] strToken = new String[10000];for(int i=0;i<100000;i++){while(st.hasMoreTokens()){strToken[i]=st.nextToken();}}Long time4=System.currentTimeMillis();System.out.println(time4-time2);
在自己的电脑上分割10000次时,StringTokenizer平均要快3ms左右(额,好像相差也不大)。
三 根据线程安全要求选用合理的数据结构
1.单线程应尽量使用HashMap、ArrayList 在单线程环境下,尽量避免使用一些针对多线程环境做了优化的集合工具。 比如,避免StringBuffer和StringBuilder,HashMap(单线程地图)和HashTable(多线程表格)等的随便使用。2.减小synchronized作用的区域同步方法的系统开销比较大,尽量在真正需要同步的地方使用synchronized关键字。
四 合理选用集合工具类
1.使用Vector、HashTable、HashMap等部分自动扩充的数据结构时,指定初始大小 查看Vector的源码,会发现定义了initialCapacity、capacityIncrement,用来指定初始容量和集合充满后的自动扩充大小, Vector在初始化时,默认的容量大小是10,大部分时候这个容量是不够的,都会进行扩充操作。 比如:public vector v = new vector(20);
2.尽量避免使用二维数组
相比一维数组,二维数组的效率比较低,相差可以达到10倍。以前做过的一些数据结构或者算法题目里面,比如一维数组替代二维数组表示坐标系,因为考虑到时空开支,可以用下标和值分别表示纵横坐标。
3.使用System.arraycopy()代替通过来循环复制数组 五 语句控制结构中的注意1.java中使用final关键字来指示一个函数为内联函数,final关键字会告诉编译器,在编译的时候考虑性能的提升 内联函数就是在程序编译时,编译器将程序中出现 的内联函数的调用表达式用内联函数的函数体来直接进行替换。理解内联函数,可以类比C语言的宏定义。这篇博文对使用final优化做了测试。
2.在文件读写,访问链接等操作中,相关的资源可以在finally块中释放
六 一些提升性能的数学计算
1.和C语言一样,乘除法如果可以使用位移,应尽量使用位移通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,
在Java中是左移、有符号右移和无符号右移运算符。位移运算符只对int值进行操作,如果不是int,编译器会报错。
a=a*4; b=b/4; //可以改为: a=a<<2; b=b>>2; //说明: //除2 = 右移1位 乘2 = 左移1位 //除4 = 右移2位 乘4 = 左移2位 //除8 = 右移3位 乘8 = 左移3位
—————分割线—————————————————
一、避免在循环条件中使用复杂表达式
在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循 环条件值不变的话,程序将会运行的更快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 例子: import java.util.vector; class cel { void method (vector vector) { for ( int i = 0 ; i < vector.size (); i++) // violation ; // ... } } 更正: class cel_fixed { void method (vector vector) { int size = vector.size () for ( int i = 0 ; i < size; i++) ; // ... } } |
二、为'vectors' 和 'hashtables'定义初始大小
jvm 为 vector 扩充大小的时候需要重新创建一个更大的数组,将原原先数组中的内容复制过 来,最后,原先的数组再被回收。可见 vector 容量的扩大是一个颇费时间的事。 通常,默认的 10 个元素大小是不够的。你最好能准确的估计你所需要的最佳大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 例子: import java.util.vector; public class dic { public void addobjects (object[] o) { // if length > 10, vector needs to expand for ( int i = 0 ; i< o.length;i++) { v.add(o); // capacity before it can add more elements. } } public vector v = new vector(); // no initialcapacity. } 更正: 自己设定初始大小。 public vector v = new vector( 20 ); public hashtable hash = new hashtable( 10 ); |
三、在 finally 块中关闭 stream
程序中使用到的资源应当被释放,以避免资源泄漏。这最好在 finally 块中去做。不管程序执行 的结果如何,finally 块总是会执行的,以确保资源的正确关闭。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 例子: import java.io.*; public class cs { public static void main (string args[]) { cs cs = new cs (); cs.method (); } public void method () { try { fileinputstream fis = new fileinputstream ( "cs.java" ); int count = 0 ; while (fis.read () != - 1 ) count++; system.out.println (count); fis.close (); } catch (filenotfoundexception e1) { } catch (ioexception e2) { } } } |
更正: 在最后一个 catch 后添加一个 finally 块
四、使用'system.arraycopy ()'代替通过来循环复制数组
'system.arraycopy ()' 要比通过循环来复制数组快的多。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 例子: public class irb { void method () { int [] array1 = new int [ 100 ]; for ( int i = 0 ; i < array1.length; i++) { array1 [i] = i; } int [] array2 = new int [ 100 ]; for ( int i = 0 ; i < array2.length; i++) { array2 [i] = array1 [i]; // violation } } } 更正: public class irb { void method () { int [] array1 = new int [ 100 ]; for ( int i = 0 ; i < array1.length; i++) { array1 [i] = i; } int [] array2 = new int [ 100 ]; system.arraycopy(array1, 0 , array2, 0 , 100 ); } } |
五、让访问实例内变量的 getter/setter 方法变成”final”
简单的 getter/setter 方法应该被置成 final,这会告诉编译器,这个方法不会被重载,所以,可 以变成”inlined” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 例子: class maf { public void setsize ( int size) { _size = size; } private int _size; } 更正: class daf_fixed { final public void setsize ( int size) { _size = size; } private int _size; } |
六、避免不需要的 instanceof 操作
如果左边的对象的静态类型等于右边的,instanceof 表达式返回永远为 true。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 例子: public class uiso { public uiso () {} } class 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" ); } } 更正: 删掉不需要的 instanceof 操作。 class dog extends uiso { void method () { dog d; system.out.println ( "dog is an uiso" ); system.out.println ( "uiso is an uiso" ); } } |
七、避免不需要的造型操作
所有的类都是直接或者间接继承自 object。同样,所有的子类也都隐含的“等于”其父类。那么, 由子类造型至父类的操作就是不必要的了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 例子: class unc { string _id = "unc" ; } class dog extends unc { void method () { dog dog = new dog (); unc animal = (unc)dog; // not necessary. object o = (object)dog; // not necessary. } } 更正: class dog extends unc { void method () { dog dog = new dog(); unc animal = dog; object o = dog; } } |
十一、在字符串相加的时候,使用 ' ' 代替 " ",如果该字符串只
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 有一个字符的话 public class str { public void method(string s) { string string = s + "d" // violation. string = "abc" + "d" // violation. } } 将一个字符的字符串替换成 ' ' public class str { public void method(string s) { string string = s + 'd' string = "abc" + 'd' } } |
十二、不要在循环中调用 synchronized(同步)方法 方法的同步需要消耗相当大的资料,在一个循环中调用它绝对不是一个好主意。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class syn { public synchronized void method (object o) { } private void test () { for ( int i = 0 ; i < vector.size(); i++) { method (vector.elementat(i)); // violation } } private vector vector = new vector ( 5 , 5 ); } 更正: 不要在循环体中调用同步方法,如果必须同步的话,推荐以下方式: import java.util.vector; public class syn { public void method (object o) { } private void test () { synchronized { //在一个同步块中执行非同步方法 for ( int i = 0 ; i < vector.size(); i++) { method (vector.elementat(i)); } } } private vector vector = new vector ( 5 , 5 ); } |
十三、将 try/catch 块移出循环
把 try/catch 块放入循环体内,会极大的影响性能,如果编译 jit 被关闭或者你所使用的是一个 不带 jit 的 jvm,性能会将下降 21%之多!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.io.fileinputstream; public class try { void method (fileinputstream fis) { for ( int i = 0 ; i < size; i++) { try { // violation _sum += fis.read(); } catch (exception e) {} } } private int _sum; } 更正: 将 try / catch 块移出循环 void method (fileinputstream fis) { try { for ( int i = 0 ; i < size; i++) { _sum += fis.read(); } } catch (exception e) {} } |
十四、对于 boolean 值,避免不必要的等式判断
将一个 boolean 值与一个 true 比较是一个恒等操作(直接返回该 boolean 变量的值). 移走对于 boolean 的不必要操作至少会带来 2 个好处: 1)代码执行的更快 (生成的字节码少了 5 个字节); 2)代码也会更加干净 。
十五、对于常量字符串,用'string' 代替 'stringbuffer'
常量字符串并不需要动态改变长度。
1 2 3 4 5 6 7 8 9 10 | 例子: public class usc { string method () { stringbuffer s = new stringbuffer ( "hello" ); string t = s + "world!" ; return t; } } 更正: 把 stringbuffer 换成 string,如果确定这个 string 不会再变的话,这将会减少运行开销提高性 |
十六、用'stringtokenizer' 代替 'indexof()' 和'substring()'
字符串的分析在很多应用中都是常见的。使用 indexof()和 substring()来分析字符串容易导致 stringindexoutofboundsexception。而使用 stringtokenizer 类来分析字符串则会容易一 些,效率也会高一些。
1 2 3 4 5 6 7 | public class ust { void parsestring(string string) { int index = 0 ; while ((index = string.indexof( "." , index)) != - 1 ) { system.out.println (string.substring(index, string.length())); } } } |
转载地址:http://nudoa.baihongyu.com/