优雅java代码效率提高的习惯(一)
这篇文章将介绍java中提高效率的一些方法。
1.循环条件中的复杂表达式应该独立出来
在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。
例子:
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++) ; // ... } }
2.使用'system.arraycopy ()'代替通过来循环复制数组
'system.arraycopy ()' 要比通过循环来复制数组快的多。
例子:
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); } }
3. 使用移位操作来代替'a / b'操作,使用移位操作代替'a * b'
例子:
public class sdiv { public static final int num = 16; public void 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; } }
更正:
public class sdiv { public static final int num = 16; public void calculate(int a) { int div = a >> 2; int div2 = a >> 3; int temp = a / 3; // 不能转换成位移操作 } }类似的,a*4,a*8可以用int mul = a << 2; int mul2 = a << 3;代替,还有取余运算a=a%8可以用a=a&7代替。但是要权衡可读性问题。除法是整数运算中效率最低的,应该尽量避免。这里补充下&运算的原理,如:
int a=129;
int b=128;
System.out.println("a 和b 与的结果是:"+(a&b));
“a”的值是129,转换成二进制就是10000001,而“b”的值是128,转换成二进制就是10000000。根据与运算符的运算规律,只有两个位都是1,结果才是1,可以知道结果就是10000000,即128。
4. 对于boolean值,避免不必要的等式判断
将一个boolean值与一个true比较是一个恒等操作(直接返回该boolean变量的值). 移走对于boolean的不必要操作至少会带来2个好处:
1)代码执行的更快 (生成的字节码少了5个字节);
2)代码也会更加干净 。
例子:
public class ueq { boolean method (string string) { return string.endswith ("a") == true; // violation } }
更正:
class ueq_fixed { boolean method (string string) { return string.endswith ("a"); } }
5. 合理使用string,stringBuffer和stringBuilder
(1).如果要操作少量的数据用 = String
(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
通过StringBuffer的构造函数来设定他的初始化容量,可以明显提升性能。
6. 不要在循环体中实例化变量
在循环体中实例化临时变量将会增加内存消耗
例子:
import java.util.vector; public class loop { void method (vector v) { for (int i=0;i < v.size();i++) { object o = new object(); o = v.elementat(i); } } }
更正:
在循环体外定义变量,并反复使用
import java.util.vector; public class loop { void method (vector v) { object o; for (int i=0;i<v.size();i++) { o = v.elementat(i); } } }
7. 在java+Oracle的应用系统开发中,java中内嵌的SQL语言应尽量使用大写形式,以减少Oracle解析器的解析负担。
8. array 数组效率最高,但容量固定,无法动态改变,ArrayList容量可以动态增长,但牺牲了效率。
9.不要使用 i % 2 == 1 来判断是否是奇数,因为i为负奇数时不成立,请使用 i % 2 != 0 来判断是否是奇数,或使用 高效式 (i & 1) != 0来判断。(让我想起了今年腾讯的笔试题)
10.货币单位计算时应该用最小单位,因为小数在java中是无法精确表示的,如:System.out.println(2.00 -1.10);//0.8999999999999999
11. int类型的溢出
我们计算一天中的微秒数:
long microsPerDay = 24 * 60 * 60 * 1000 * 1000;// 正确结果应为:86400000000
System.out.println(microsPerDay);// 实际上为:500654080
问题在于计算过程中溢出了。这个计算式完全是以int运算来执行的,并且只有在运算完成之后,其结果才被提升为long,而此时已经太迟:计算已经溢出。
解决方法使计算表达式的第一个因子明确为long型,这样可以强制表达式中所有的后续计算都用long运算来完成,这样结果就不会溢出:
long microsPerDay = 24L * 60 * 60 * 1000 * 1000;
12. 尽量使用final修饰符
带有final修饰符的类是不可派生的。在JAVA核心API中,有许多应用final的例子,例如 java.lang.String。为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高 50%。
13. 用x++,x+=1代替x=x+1能提高效率
14.条件判断时将可能性大的放在前面
15. 出于安全考虑,判断语句应该将变量作为比较的对象
如if(a==3)代替为if(3==a)
16. 冒泡排序的时候设置一个boolean类型的交换标记可以提高速度(排序好不用再遍历)
例子:
package com.wws.yy; import java.util.Random; public class BubbleSortClient { public static void main(String[] args) { //构造数据 int[] arr = constructDataArray(15); System.out.println("---------排序前-----------"); printArrayData(arr); //冒泡排序 bubbleSort2(arr); System.out.println("---------排序后-----------"); printArrayData(arr); } //构造数据 public static int[] constructDataArray(int length){ int[] arr = new int[length]; Random random = new Random(); for(int i=0;i<length;i++){ arr[i] = random.nextInt(length); } return arr; } /** * 冒泡排序方法----第一种方法 * @param arr */ public static int[] bubbleSort(int[] arr){ for(int i=0; i<arr.length;i++){ for(int j=i+1;j<arr.length;j++){ if(arr[i] > arr[j]){ //交换数据 int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } return arr; } /** * 冒泡排序方法----第二种方法 * @param arr */ public static int[] bubbleSort2(int[] arr){ for(int i=0;i<arr.length;i++){ for(int j=0;j<arr.length-1-i;j++){ if(arr[j] > arr[j+1]){ //数据交换 int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr; } //打印数据 public static void printArrayData(int[] arr){ for(int d :arr){ System.out.print(d + " "); } System.out.println(); } }
优化后:
import java.util.Random; public class BubbleSortClient { public static void main(String[] args) { //构造数据 int[] arr = constructDataArray(15); System.out.println("---------排序前-----------"); printArrayData(arr); //冒泡排序 bubbleSort4(arr); System.out.println("---------排序后-----------"); printArrayData(arr); } //构造数据 public static int[] constructDataArray(int length){ int[] arr = new int[length]; Random random = new Random(); for(int i=0;i<length;i++){ arr[i] = random.nextInt(length); } return arr; } /** * 引入标志位,默认为true * 如果前后数据进行了交换,则为true,否则为false。如果没有数据交换,则排序完成。 * @param arr */ public static int[] bubbleSort4(int[] arr){ boolean flag = true; int n = arr.length; while(flag){ flag = false; for(int j=0;j<n-1;j++){ if(arr[j] >arr[j+1]){ //数据交换 int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; //设置标志位 flag = true; } } n--; } return arr; } //打印数据 public static void printArrayData(int[] arr){ for(int d :arr){ System.out.print(d + " "); } System.out.println(); } }