package android.util; import com.android.internal.util.ArrayUtils; /** * SparseArrays 利用integer去管理object对象。不像一个正常的object对象数组,它能在索引数中快速的查找到所需的结果。(这 * 句话是音译,原意是能在众多索引数中“撕开一个缺口”,为什么原文这么表达?下面会慢慢说清楚。)它比HashMap去通过Integer索引 * 查找object对象时在内存上更具效率,不仅因为它避免了用来查找的自动“装箱”的keys,并且它的数据结构不依赖额外的对象去 * 各个映射中查找匹配。 * * SparseArrays map integers to Objects. Unlike a normal array of Objects, * there can be gaps in the indices. It is intended to be more memory efficient * than using a HashMap to map Integers to Objects, both because it avoids * auto-boxing keys and its data structure doesn't rely on an extra entry object * for each mapping. * * 请注意,这个容器会保持它的映射关系在一个数组的数据结构中,通过二分检索法驱查找key。(这里我们终于知道,为何这个工具类中, * 提供的添加映射关系的操作中,key的类型必须是integer。因为二分检索法,将从中间“切开”,integer的数据类型是实现这种检索过程的保证。) * * 如果保存大量的数据,这种数据结构是不适合的,换言之,SparseArray这个工具类并不应该用于存储大量的数据。这种情况下,它的效率 * 通常比传统的HashMap更低,因为它的查找方法并且增加和移除操作(任意一个操作)都需要在数组中插入和删除(两个步骤才能实现)。 * * 如果存储的数据在几百个以内,它们的性能差异并不明显,低于50%。 * * (OK,那么光看Android官方的介绍我们就有初步结论了,大量的数据我们相对SparseArray会优先选择HashMap,如果数据在几百个这个数目, * 那么选择它们任意一个去实现区别不大,如果数量较少,就选择SparseArray去实现。 其实如果我们理解了二分法,就很容易了SparseArray的 * 实现原理,以及SparseArray和HashMap它们之间的区别了。) * * <p>Note that this container keeps its mappings in an array data structure, * using a binary search to find keys. The implementation is not intended to be appropriate for * data structures * that may contain large numbers of items. It is generally slower than a traditional * HashMap, since lookups require a binary search and adds and removes require inserting * and deleting entries in the array. For containers holding up to hundreds of items, * the performance difference is not significant, less than 50%.</p> * * * 为了提高性能,这个容器包含了一个实现最优的方法:当移除keys后为了立刻使它的数组紧密,它会“遗留”已经被移除(标记了要删除)的条目(entry) 。 * 所被标记的条目(entry)(还未被当作垃圾回收掉前)可以被相同的key复用,也会在垃圾回收机制当作所有要回收的条目的一员被回收,从而使存储的数组更紧密。 * * (我们下面看源码就会发现remove()方法其实是调用delete()方法的。印证了上面这句话所说的这种优化方法。 * 因为这样,能在每次移除元素后一直保持数组的数据结构是紧密不松散的。) * * 垃圾回收的机制会在这些情况执行:数组需要扩充,或者映射表的大小被恢复,或者条目值被重新检索后恢复的时候。 * * <p>To help with performance, the container includes an optimization when removing * keys: instead of compacting its array immediately, it leaves the removed entry marked * as deleted. The entry can then be re-used for the same key, or compacted later in * a single garbage collection step of all removed entries. This garbage collection will * need to be performed at any time the array needs to be grown or the the map size or * entry values are retrieved.</p> * * 当调用keyAt(int)去获取某个位置的key的键的值,或者调用valueAt(int)去获取某个位置的值时,可能是通过迭代容器中的元素 * 去实现的。 * * <p>It is possible to iterate over the items in this container using * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending * order in the case of <code>valueAt(int)<code>.</p> */ public class SparseArray<E> implements Cloneable { //... }
####Android开发中高效的数据结构 android开发中,在java2ee或者android中常用的数据结构有Map,List,Set,但android作为移动平台,有些api(很多都是效率问题)显然不够理想,本着造更好轮子的精神,android团队编写了自己的api用来代替java api 1、SimpleArrayMap<K,V>与ArrayMap<K,V> 实质上ArrayMap继承自SimpleArrayMap,主要是为了实现像HashMap一样的api方法,让习惯使用HashMap的开发者感觉不到差异,本质上是SimpleArrayMap+Map的再封装。 一般来说使用这2个类主要来代替HashMap,因为他们比HashMap更加高效,对内存也进行了优化。 2、SparseArray<T>与SparseArrayCompat<T>和LongSparseArray<T> 这3个类中,前2个基本上是同一类,只不过第二个类有removeAt方法,第三个是Long类型的。 这3个类也是用来代替HashMap,只不过他们的键(key)的类型是整型Integer或者Long类型,在实际开发中,如月份缩写的映射,或者进行文件缓存映射,viewHolder都特别适用 3、AtomicFile AtomicFile首先不是用来代替File的,而是作为File的辅助类从在, AtomicFile的作用是实现事务性原子操作,即文件读写必须完整,适合多线程中的文件读写操作。 用来实现多线程中的文件读写的安全操作 ---- #####用SparseArray代替HashMap SparseArray是android提供的一个工具类,它可以用来替代hashmap进行对象的存储,其内部实现了一个矩阵压缩算法,很适合存储稀疏矩阵的。 PS:support包中还提供了兼容的类SparseArrayCompat,基本和SparseArray是同一个类,只不过第二个类有removeAt方法 针对源码的详细分析:[http://stormzhang.com/android/2013/08/01/android-use-sparsearray-for-performance-optimization/](http://stormzhang.com/android/2013/08/01/android-use-sparsearray-for-performance-optimization/ "http://stormzhang.com/android/2013/08/01/android-use-sparsearray-for-performance-optimization/") 一、和Hashmap的对比 既然android推荐用这个东西,自然有用它的道理。其内部实现了压缩算法,可以进行矩阵压缩,大大减少了存储空间,节约内存。此外它的查找算法是二分法,提高了查找的效率。 替换原则: 1> 如果用到了: HashMap<Integer, E> hashMap = new HashMap<Integer, E>(); 可以替换为:SparseArray<E> sparseArray = new SparseArray<E>(); 2> 如果用到了:HashMap<Integer, Boolean> hashMap = new HashMap<Integer, Boolean> 可以替换为:SparseBooleanArray array = new SparseBooleanArray(); 3> 如果用到了:HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer> 可以替换为:SparseIntArray array = new SparseIntArray(); 二、用法 既然是键值对那么就有增删改查,但要记得先初始化: Button btn = null; // 测试view,无意义 Button btn02 = null; // 测试view,表示新增的对象 final int KEY = 1; /* * SparseArray指的是稀疏数组(Sparse * array),所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用 * 。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。 */ SparseArray<View> array = new SparseArray<View>(); 2.1 增加数据 /* 增加数据 */ //public void put(int key, E value) {} array.put(KEY, btn); //public void append(int key, E value){} array.append(KEY, btn); 2.2 修改数据 /* 修改数据 */ //在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。 //public void put(int key, E value) array.put(KEY, btn); //public void setValueAt(int index, E value) array.setValueAt(KEY, btn02); 2.3 查找数据 /* 查找数据 */ //public E get(int key) array.get(KEY); //public E get(int key, E valueIfKeyNotFound) //其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。 array.get(KEY, btn); // 如果这个key找不到value,那么就返回第二个参数。和default value一样 2.4 通过位置,查找键的值 // 查看第几个位置的键: //public int keyAt(int index) array.keyAt(1); // 如果找不到就返回-1 2.5 通过位置,查找值 // 查看第几个位置的值: //public E valueAt(int index) array.valueAt(1); // 查看值所在位置,没有的话返回-1: //public int indexOfValue(E value) array.indexOfValue(btn); 三、测试代码 package com.kale.pictest; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.View; import android.widget.Button; /** * @author: * @description : * @web : http://stormzhang.com/android/2013/08/01/android-use-sparsearray-for-performance-optimization/ * @date :2015年1月19日 */ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); Log.d("TAG", "Max memory is " + maxMemory + "KB"); Button btn = null; // 测试view,无意义 Button btn02 = null; // 测试view,表示新增的对象 final int KEY = 1; /* * SparseArray指的是稀疏数组(Sparse * array),所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用 * 。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。 */ SparseArray<View> array = new SparseArray<View>(); /* 增加数据 */ //public void put(int key, E value) {} array.put(KEY, btn); //public void append(int key, E value){} array.append(KEY, btn); /* 修改数据 */ //在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。 //public void put(int key, E value) array.put(KEY, btn); //public void setValueAt(int index, E value) array.setValueAt(KEY, btn02); /* 查找数据 */ //public E get(int key) array.get(KEY); //public E get(int key, E valueIfKeyNotFound) //其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。 array.get(KEY, btn); // 如果这个key找不到value,那么就返回第二个参数。和default value一样 // 查看第几个位置的键: //public int keyAt(int index) array.keyAt(1); // 如果找不到就返回-1 // 查看第几个位置的值: //public E valueAt(int index) array.valueAt(1); // 查看值所在位置,没有的话返回-1: //public int indexOfValue(E value) array.indexOfValue(btn); SparseBooleanArray d; } } 测试代码四: public class FragmentPagerItemAdapter extends FragmentPagerAdapter { private final FragmentPagerItems mPages; private final SparseArrayCompat<WeakReference<Fragment>> mHolder; public FragmentPagerItemAdapter(FragmentManager fm, FragmentPagerItems pages) { super(fm); mPages = pages; mHolder = new SparseArrayCompat<>(pages.size()); } @Override public int getCount() { return mPages.size(); } @Override public Fragment getItem(int position) { return getPagerItem(position).instantiate(mPages.getContext(), position); } @Override public Object instantiateItem(ViewGroup container, int position) { Object item = super.instantiateItem(container, position); if (item instanceof Fragment) { mHolder.put(position, new WeakReference<Fragment>((Fragment) item)); } return item; } @Override public void destroyItem(ViewGroup container, int position, Object object) { mHolder.remove(position); super.destroyItem(container, position, object); } @Override public CharSequence getPageTitle(int position) { return getPagerItem(position).getTitle(); } @Override public float getPageWidth(int position) { return super.getPageWidth(position); } public Fragment getPage(int position) { final WeakReference<Fragment> weakRefItem = mHolder.get(position); return (weakRefItem != null) ? weakRefItem.get() : null; } protected FragmentPagerItem getPagerItem(int position) { return mPages.get(position); } }
1,SparseArray的原理是二分检索法,也因此key的类型都是整型。
2,(HashMap和SparseArray比较)当存储大量数据(起码上千个)的时候,优先选择HashMap。如果只有几百个,用哪个区别不大。如果数量不多,优先选择SparseArray。
3,SparseArray有自己的垃圾回收机制。