现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升,
但是在开发中注意这些,更多的是保持一种性能优先的意识,养成良好的编码习惯。
一 条件控制语句中的优化
1.在循环中应该避免使用复杂的表达式。
在循环中,循环条件会被反复计算,应该避免把一些计算放在循环进行的部分中,程序将会运行的更快。
比如:
1
2
3
4
|
for
(
int
i=
0
;i<list.size();i++)
可以改为
//我的电脑上,测试数量级在10^7,速度提升一倍。
for
(
int
i=
0
,len=list.size();i<len;i++)
|
二 更好的使用变量
1.合理使用final修饰符
注意fina修饰变量或者方法时的用法不同。很多时候使用"static final 变量"格式是很好的选择。
2.尽量避免不必要的创建,同时避免使用随意使用静态变量
GC通常是不会回收静态变量对象所占有的内存,所以要合理的使用静态区域代码。
3.使用基本数据类型代替对象
Java中,String对象主要由3部分组成:
char数组、偏移量和String的长度。
1
2
|
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。
在代码中验证一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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,大部分时候这个容量是不够的,都会进行扩充操作。
比如:
1
|
public
vector v =
new
vector(
20
);
|
2.尽量避免使用二维数组
相比一维数组,二维数组的效率比较低,相差可以达到10倍。
以前做过的一些数据结构或者算法题目里面,一维数组替代二维数组表示坐标系,因为考虑到时空开支,可以用下标和值分别表示纵横坐标。
3.使用System.arraycopy()代替通过来循环复制数组
五 语句控制结构中的注意
1.java中使用final关键字来指示一个函数为内联函数,final关键字会告诉编译器,在编译的时候考虑性能的提升
内联函数就是在程序编译时,编译器将程序中出现 的内联函数的调用表达式用内联函数的函数体来直接进行替换。
理解内联函数,可以类比C语言的宏定义。
这篇博文对使用final优化做了测试。http://blog.csdn.net/inkfish/article/details/4849028
2.在文件读写,访问链接等操作中,相关的资源可以在finally块中释放
六 一些提升性能的数学计算
1.和C语言一样,乘除法如果可以使用位移,应尽量使用位移
通常如果需要乘以或除以2的n次方,都可以用移位的方法代替,
在Java中是左移、有符号右移和无符号右移运算符。位移运算符只对int值进行操作,如果不是int,编译器会报错。
1
2
3
4
5
6
7
8
9
|
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.在 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 块
2.使用'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
);
}
}
|
3.避免不需要的 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"
);
}
}
|
4.在字符串相加的时候,使用 ' ' 代替 " ",如果该字符串只
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'
}
}
|
5.不要在循环中调用 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
);
}
|
6.对于 boolean 值,避免不必要的等式判断
将一个 boolean 值与一个 true 比较是一个恒等操作(直接返回该 boolean 变量的值). 移走对于
boolean 的不必要操作至少会带来 2 个好处:
1)代码执行的更快 (生成的字节码少了 5 个字节);
2)代码也会更加干净 。
7.对于常量字符串,用'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 不会再变的话,这将会减少运行开销提高性
|
本文转自邴越博客园博客,原文链接:http://www.cnblogs.com/binyue/p/4213535.html,如需转载请自行联系原作者