为什么要学习数据结构?
有个人上少林寺拜师学艺。大师指点说武功重在内力,应该先炼内功,然后再练外功,然后大师让他拿一根管子对着半缸水吹气,说什么时候能把缸里的水吹到溢出来,就证明内功达到至高境界了。于是他就日复一日地刻苦练习,三年过去了,没有任何效果,但他想:既然师父那么说就一定有他的道理。
于是继续修炼。就这样过了十年,还是不行。终于,他失望了,决定不再修炼准备回家。到家碰到他爹,他爹就问他,说你拜师学艺学了十年学得怎么样啊? 他觉得很没面子,遂失落地低头叹了口气,等他抬头再看——他爹不见了……
什么是性能优化?
一款app除了要有令人惊叹的功能和令人发指的交互之外,在性能上也应该追求丝滑的要求,这样才能更好地提高用户体验。
线性数据结构
Hash表
结合了数组和链表的优点,增删改查都很快。
HashMap
SparseArray
1.key数组即存储int型的key,是按顺序排序的,因为插入数据的时候是根据二分查找寻找待插入数据的下标的。
2.插入数据时先根据二分查找寻找下标index,在index位置插入数据,然后移动index位置后面的元素
比如在下面的数据中插入key为54的数据
先二分查找找到54的index是在50和100之间,然后在index位置分别插入key和value
HashMap 与 SparseArray
HashMap 和 SparseArray性能对比
public class HashMapSparseArrayActivity extends AppCompatActivity {
private String TAG = "HashMapSparseArrayActivity";
private Button btnHashMap;
private Button btnSparseArray;
private Button btnTimeuse;
private HashMap hashMap = new HashMap();
private SparseArray sparseArray = new SparseArray();
final int LENGTH = 100000;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hashmapspasearray);
btnHashMap = findViewById(R.id.hashmap);
btnSparseArray = findViewById(R.id.spasearray);
btnTimeuse = findViewById(R.id.timeuse);
btnHashMap.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
long starttime = System.currentTimeMillis();
for (int i = 0; i < LENGTH; i++) {
hashMap.put(i, new byte[10]);
}
long endtime = System.currentTimeMillis();
Log.i(TAG, "hashmap caused: " + (endtime - starttime) + "ms");
return null;
}
}.execute();
}
});
btnSparseArray.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
long starttime = System.currentTimeMillis();
for (int i = 0; i < LENGTH; i++) {
sparseArray.put(i, new byte[10]);
}
long endtime = System.currentTimeMillis();
Log.i(TAG, "sparsearray caused: " + (endtime - starttime) + "ms");
return null;
}
}.execute();
}
});
btnTimeuse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//时间性能
timerUseHashMap();
timerUseSparseArray();
}
});
}
public void timerUseHashMap() {
long starttime = System.currentTimeMillis();
for (int i = 0; i < LENGTH; i++) {
hashMap.get(i);
}
long endtime = System.currentTimeMillis();
Log.i(TAG, "hashmap caused: " + (endtime - starttime) + "ms");
}
public void timerUseSparseArray() {
long starttime = System.currentTimeMillis();
for (int i = 0; i < LENGTH; i++) {
sparseArray.get(i);
}
long endtime = System.currentTimeMillis();
Log.i(TAG, "sparsearray caused: " + (endtime - starttime) + "ms");
}
}
内存
两个按钮操作,分别执行:添加100000(10万)个数字到HashMap和SparseArray中。然后查看内存使用情况。
初始内存:
内存:6.6 MB
点击HashMap按钮后:
内存:14.1 MB,即HashMap占用内存7.5MB
点击SparseArray按钮后:
内存:18.2 MB,即SparseArray占用内存4.1MB
可以看出SparseArray使用的内存远远小于HashMap.
时间
点击HashMap按钮和SparseArray按钮后,再点击测试时间按钮:
hashmap caused: 1663ms
sparsearray caused: 6ms
可以看到读取相同数量的数据时,SparseArray消耗的时间比HashMap少很多。
数据结构如何选择
1.链表对于删除和新增数据操作比较快,数组对于查找和修改数据操作比较快,所以删除和新增数据操作多的场景用LinkedList,查找和修改操作多的场景用ArrayList。
2.SparseArray的key只能是int类型的,所以如果key不是int类型的,那只能选择HashMap ,但Android中有一种应用场景,可以在key是String类型的情况下也用SparseArray,因为Android会在编译的时候把所有的资源编译为一个唯一的int型的资源id(包括String类型的资源),所以可以将String类型的key都写在strings.xml文件里,比如string1、string2、string3,然后将R.string.string1,R.string.string2,R.string.string3作为key存入SparseArray中。
3.本文主要讲解如何对ArrayList和LinkedList、HashMap和SparseArray进行选择,其他数据结构(比如栈,队列,堆,图,红黑树,字典树,B树等等)也都是根据具体的应用场景进行选择的。