SparseArray,SparseBooleanArray和SparseIntArray

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有自己的垃圾回收机制。

 

posted on 2017-11-22 10:27  wp7ers  阅读(3476)  评论(0编辑  收藏  举报