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

  • 相关阅读:
    WebGL 理论基础 二维缩放
    Cesium热力图实现 wenglabs
    颜色 学习法
    百度、高德、谷歌、火星、wgs84(2000)地图坐标相互转换的JS实现_一碗老面i的博客
    JS 并行计算
    AJAX 数据传输进度条设计与实现_qingyafan的博客
    WebGL 理论基础 二维平移
    3D热力图的简单实现
    WebGL 理论基础 二维矩阵
    Cesium imageryLayers,entity remove 删除,漏删问题_一碗老面i的博客
  • 原文地址:https://www.cnblogs.com/mamamia/p/7877995.html
Copyright © 2020-2023  润新知