1、如果需要用很多对象时可以使用Cache,经常写Java的人可能会使用HashMap去实现Cache,但当Key是Integer的值时,Android为我们定义了一个更有效率的一个类:SparseArray类,它具有比HashMap更高的效率。
注意:1.对于SparseArray类的一些解释:SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch),今天在看这个class的实现时发现一个很好的设计:
public void remove(int key) {
delete(key);
}
remore和delete都实现了,功能当然是一样的,对用户来说,想用delete,和remove的都可以。
起折半查找算法也写的非常棒:
private static int binarySearch(int[] a, int start, int len, int key) {
int high = start + len, low = start - 1, guess;
while (high - low > 1) {
guess = (high + low) / 2;
if (a[guess] < key)
low = guess;
else
high = guess;
}
if (high == start + len)
return ~(start + len);
else if (a[high] == key)
return high;
else
return ~high;
同步的HashMap和Hashtable的性能相当
在高并发的情况下,HashMap可能会出现卡死的情况,原因是HashMap属于非线程安全的,具体的分析可以参看第三点。
出现高并发操作HashMap的,最好改用ConcurrentHashMap代替,两者的性能比较可参看第二点。
2、HashMap和ConcurrentHashMap的并发性能。
ConcurrentHashMap的性能比同步的HashMap快一倍左右,同步的HashMap和Hashtable的性能相当。
测试数据参见:http://blog.youkuaiyun.com/java2000_net/article/details/3373181
3、不正当使用HashMap导致cpu100%的问题追究。
过程参见:http://code.alibabatech.com/blog/dev_related_969/hashmap-result-in-improper-use-cpu-100-of-the-problem-investigated.html
2、不同的API版本针对不同的功能已经做了优化,有些函数被弃用,启用新的调用函数,因此,在实现不同的API版本的时候,可针对不同的API进行不同程度的优化,个人觉得这种优化成本高,收益小。如:在HONEYCOMB中,sparseArray进行了优化,因此在使用高版本的sdk时,使用新的函数,对于低版本使用旧的兼容函数实现。
<span style="font-family:SimSun;font-size:16px;">if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
sparseArray.removeAt(1); // API level 11 and above
} else {
int key = sparseArray.keyAt(1); // 默认实现效率更低
sparseArray.remove(key);
}</span>
3、性能并不仅仅指程序的执行速度,也包括用户交互体验,用户在使用软件时的响应速度等。ui线程用来处理按键消息(如:View.onKeyDown()或Activity.onKeyLongPress()),图形绘画(如:View.onDraw())以及生命周期时间的发生(如:onCreate(),onStart()等),其它不重要的代码尽量放到非ui线程去处理,避免出现ANR(Application Not Responding)的出现。
在Activity中,尽量减少Activity的启动时间是非常重要的,尽量减小在onCreate(),onStart(),onResume()中执行过于耗时的操作,如,Activeity的onCreate()方法中一般使用setContentView或者其它的方式去添加视图inflate视图,而inflate视图的过程是比较耗时的,因此UI界面的设计非常重要,在xml中,尽量使用简洁的布局可以减少布局的加载时间,有一些常用的操作,比如:
(1)使用RelativeLayout代替LinearLayout
(2)view的layout尽量使用viewroot去实现延迟加载,加快ui展示
具体详看我之前的文章:http://blog.youkuaiyun.com/billpig/article/details/6672588
4、在针对网络和文件操作时应处理低网速及io操作的情况,sd卡有不同的处理速度,如果我们的程序很依赖于sd卡的性能,那就必须重点测试下不同厂家的sd卡
5、在2.3后可以使用StrictMode去辅助测试程序在低速率下或者其它情况下的用户体验和程序的健壮性,注意,这个仅在开发时使用,发布版本时必须去掉。举个例子如下:让你的代码执行的效率低点:
public static BigInteger computeRecursivelyWithCache(int n)
{
StrictMode.noteSlowCall(“computeRecursivelyWithCache”); // message can be
anything
SparseArray<BigInteger> cache = new SparseArray<BigInteger>();
return computeRecursivelyWithCache(n, cache);
}
…
}
注意:对于StrictMode(严苛模式)的理解:http://blog.youkuaiyun.com/tonyfield/article/details/8238251
6、数据库优化
我们在使用数据库的时候,经常会使用到CREATE TABLE, INSERT INTO语句等,举个例子:
SQLiteDatabase db = SQLiteDatabase.create(null);
db.execSQL(“CREATE TABLE cheese (name TEXT, origin TEXT)”);
db.execSQL(“INSERT INTO cheese VALUES (‘Roquefort’, ‘Roquefort-sur-Soulzon’)”);
db.close();
执行SQLite语句的时候会花一定的时间,这些语句需要:解释或者编译或者执行而且而且String是不可变的,因此在构成该语句的时候,又进行了很多对象的创建操作,导致性能的问题。这里通过举个例子去详细讲解下:
public class Cheeses {
private static final String[] sCheeseNames = {
“Abbaye de Belloc”,
“Abbaye du Mont des Cats”,
…
“Vieux Boulogne”
};
private static final String[] sCheeseOrigins = {
“Notre-Dame de Belloc”,
“Mont des Cats”,
…
“Boulogne-sur-Mer”
};
private final SQLiteDatabase db;
public Cheeses () {
db = SQLiteDatabase.create(null); // memory-backed database
db.execSQL(“CREATE TABLE cheese (name TEXT, origin TEXT)”);
}
public void populateWithStringPlus () {
int i = 0;
for (String name : sCheeseNames) {
String origin = sCheeseOrigins[i++];
String sql = “INSERT INTO cheese VALUES(\”” + name + ”\”,\”” + origin +”\”)”;
db.execSQL(sql);
}
}
}
在执行populateWithStringPlus的时候,会创建大量的String对象以及执行execSQL。针对第一点,最明显的优化的手段就是少用String的+操作,使用String.format或者StringBuilder进行代替:
public void populateWithStringFormat () {
int i = 0;
for (String name : sCheeseNames) {
String origin = sCheeseOrigins[i++];
CHAPTER 1: Optimizing Java Code 27
String sql = String.format(“INSERT INTO cheese VALUES(\”%s\”,\”%s\”)”, name,
origin);
db.execSQL(sql);
}
}
public void populateWithStringBuilder () {
StringBuilder builder = new StringBuilder();
builder.append(“INSERT INTO cheese VALUES(\””);
int resetLength = builder.length();
int i = 0;
for (String name : sCheeseNames) {
String origin = sCheeseOrigins[i++];
builder.setLength(resetLength); // reset position
builder.append(name).append(“\”,\””).append(origin).append(“\”)”); // chain
calls
db.execSQL(builder.toString());
}
}
当添加爱650条数据时,String.format花436ms的时候,而StringBuilder花了371ms,后者效率更佳,但不是很明显。优化的第二个方法:因为每条插入语句都差不多,只不过数据有变化而已,因此可以使用compileStatement创建语句进行复用,在循环外仅编译一次语句:
public void populateWithCompileStatement () {
SQLiteStatement stmt = db.compileStatement(“INSERT INTO cheese VALUES(?,?)”);
int i = 0;
for (String name : sCheeseNames) {
String origin = sCheeseOrigins[i++];
stmt.clearBindings();
stmt.bindString(1, name); // replace first question mark with name
stmt. bindString(2, origin); // replace second question mark with origin
stmt.executeInsert();
}
}
8、读取数据库时尽量只选取所需要的数据去读,不要全部都读
public void iterateBothColumns () {
Cursor c = db.query(“cheese”, null, null, null, null, null, null);
if (c.moveToFirst()) {
do {
} while (c.moveToNext());
}
c.close();
}
public void iterateFirstColumn () {
Cursor c = db.query(“cheese”, new String[]{“name”}, null, null, null, null, null); // 不一样的地方
if (c.moveToFirst()) {
do {
} while (c.moveToNext());
}
c.close();
}
如第一个方法读取所有数据,而第二个方法读取name的数据,后者的所花的时间比前者少。