程序的性能受代码质量的直接影响。在本文中,主要介绍一些代码编写的小技巧和惯例,这些技巧有助于在代码级别上提升系统性能。
1、慎用异常在Java软件开发中,经常使用 try-catch 进行错误捕获,但是,try-catch 语句对系统性能而言是非常糟糕的。虽然在一次 try-catch中,无法察觉到它对性能带来的损失,但是,一旦try-catch被应用于循环之中,就会给系统性能带来极大的伤害。
以下是一段将try-catch应用于for循环内的示例
public void test() { int a = 0; try { for (int i = 0; i < 1000000; i++) { a = a + 1; System.out.println(i); } } catch (Exception e) { e.printStackTrace(); } }这段代码我运行时间是 27211 ms。如果将try-catch移到循环体外,那么就能提升系统性能,如下代码
public void test() { int a = 0; for (int i = 0; i < 1000000; i++) { try { a = a + 1; System.out.println(i); } catch (Exception e) { e.printStackTrace(); } } }运行耗时 15647 ms。可见tyr-catch对系统性能的影响。
2、使用局部环境调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。
下面是一段测试用例
// private static int a = 0; public static void main(String[] args) { int a = 0; long start = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { a = a + 1; System.out.println(i); } System.out.println(System.currentTimeMillis() - start); }运行结果很明显,使用静态变量耗时15677ms,使用局部变量耗时13509ms。由此可见,局部变量的访问速度高于类的成员变量。
3、位运算代替乘除法在所有的运算中,位运算是最为高效的。因此,可以尝试使用位运算代替部分算术运算,来提高系统的运行速度。
比如在HashMap的源码中使用了位运算
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final int MAXIMUM_CAPACITY = 1 << 30;对于整数的乘除运算优化
a*=2 a/=2用位运算可以写为
a<<=1 a>>=1 4、替换switch关键字 switch 语句用于多条件判断, switch 语句的功能类似于 if-else 语句,两者性能也差不多。因此,不能说 switch 语句会降低系统的性能。但是,在绝大部分情况下,switch 语句还是有性能提升空间的。
来看下面的例子:
public static void main(String[] args) { long start = System.currentTimeMillis(); int re = 0; for (int i = 0;i<1000000;i++){ re = switchInt(i); System.out.println(re); } System.out.println(System.currentTimeMillis() - start+"毫秒");//17860 } public static int switchInt(int z){ int i = z%10+1; switch (i){ case 1:return 3; case 2:return 6; case 3:return 7; case 4:return 8; case 5:return 10; case 6:return 16; case 7:return 18; case 8:return 44; default:return -1; } }就分支逻辑而言,这种 switch 模式的性能并不差。但是如果换一种新的思路替代switch,实现相同的程序功能,name性能就能有很大的提升空间。
public static void main(String[] args) { long start = System.currentTimeMillis(); int re = 0; int[] sw = new int[]{0,3,6,7,8,10,16,18,44}; for (int i = 0;i<1000000;i++){ re = arrayInt(sw,i); System.out.println(re); } System.out.println(System.currentTimeMillis() - start+"毫秒");//12590 } public static int arrayInt(int[] sw,int z){ int i = z%10+1; if (i>7 || i<1){ return -1; }else { return sw[i]; } }以上代码使用全新的思路,使用一个连续的数组代替了 switch 语句。因为对数据的随机访问是非常快的,至少好于 switch 的分支判断。通过实验,使用switch的语句耗时17860ms,使用数组的实现只耗时12590ms,提升了5s多。在软件开发中,换一种思路可能会取得更好的效果,比如使用数组替代switch语句就是就是一个很好的例子。
5、一维数组代替二维数组由于数组的随机访问的性能非常好,许多JDK类库,如ArrayList、Vector等都是使用了数组作为其数组实现。但是,作为软件开发人员也必须知道,一位数组和二维数组的访问速度是不一样的。一位数组的访问速度要优于二维数组。因此,在性能敏感的系统中要使用二维数组的,可以尝试通过可靠地算法,将二维数组转为一维数组再进行处理,以提高系统的响应速度。
6、提取表达式在软件开发过程中,程序员很容易有意无意让代码做一些“重复劳动”,在大部分情况下,由于计算机的告诉运行,这些“重复劳动”并不会对性能构成太大的威胁,但若将系统性能发挥到极致,提取这些“重复劳动”相当有意义。
来看下面的测试用例:
@Test public void test(){ long start = System.currentTimeMillis(); ArrayList list = new ArrayList(); for (int i = 0;i<100000;i++){ System.out.println(list.add(i)); } //以上是为了做准备 for (int i = 0;i<list.size();i++){ System.out.println(list.get(i)); } System.out.println(System.currentTimeMillis() - start);//5444 }