SparseArray具体解释,我说SparseArray,你说要!

可能在Android 中使用HashMap 的时候看到过提示。

HashMap<Integer,Bitmap> mp = new HashMap<Integer,Bitmap>();

提示:Use new SparseArray<Bitmap>(...) instead for better performance意思是,使用 SparseArray 将获得更好的性能

(注:这个提示我再eclipse 中见过,而在studio 中并没有看到过这种提示)

那么SparseArray这个类是干嘛使得,有什么长处,特性呢?

ok。我们从以下几点介绍下SparseArray 这个类。

1.武功秘籍之SparseArray(SparseArray文档介绍)

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.

(译文+个人理解)

SparseArrays不同于普通的对象数组。there can be gaps in the indices.(指针中能够存在空白?注:这句我不太理解,不知道怎样翻译,是说SparseArrays 是不连续存储?)。它的目的是比使用从整数映射对象的HashMap(简单来说就是 HashMap<Integer,Object>)有更有效的使用内存。

同一时候也避免auto-boxing(自己主动装箱)(注:auto-boxing(自己主动装箱):将原始类型封装为对象类型。比方把int类型封装成Integer类型。)和数据结构对每条映射不依赖额外的Entry 对象(注:这里是和HashMap做的对照。在HashMap有须要引入Entry<K,V>,而SparseArrays比較简单,里面是两个一维数组 int[] mKeys; 和 Object[] mValues)

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%.

注意,这个容器在数组数据结构中维持一个映射关系。使用二分查找法来找到key.它的目标不是为了适应数据结构中包括大量数据的情况。

通常情况下要比传统的HashMap慢,由于查找是用二分查找法搜索,加入和删除须要对数组进行加入和删除。

对于有几百条的数据的容器。性能差异不大,不超过50%(注:这里我理解的是, SparseArrays 的优势更体如今小数量上)

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.

为了提高性能,该容器提供了一个优化:当删除key键时。不是立刻删除这一项,而是留下须要删除的选项给一个删除的标记。

该条目能够被又一次用于同样的key,或者被单个垃圾收集器逐步删除完所有的条目后压缩。在不论什么时候。当数组须要增长(注:这里我理解为 put、append之类的操作)或者Map 的长度、entry的value须要被检索的时候,该垃圾收集器就会运行(注:这里能够从代码中发现。google在相应的方法中调用 gc() 方法)。

It is possible to iterate over the items in this container using

* {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using

* keyAt(int) 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 valueAt(int).

在此使用此容器时,能够迭代遍历当中的item.. keyAt(int) 返回升序下的key .

valueAt(int) 返回升序状态下。key 相应的value值。

2.SparseArray,HashMap 不服?SOLO

2.1 key,value 类型的比較

HashMap                   key:随意类型         value:随意类型
SparseArray               key:Integer         value:随意类型
SparseBooleanArray        key:Integer         value:Boolean类型。
SparseIntArray            key:Integer         value:Integer类型。
LongSparseArray           key:Long            value:随意类型

依据需求的不同,找到合适的方法才是上上策。

2.2 内部存储的比較

SparseArray  :int[] mKeys 和 Object[] mValues 来存储 key 和 value
HashMap      :内部存储须要用到 `Entry<K,V>`

2.3 运行速度比較

这里不打算做代码的介绍了。这篇文章里有比較具体的介绍,这里我仅仅打算总结下。(http://www.open-open.com/lib/view/open1402906434918.html

  1. 创建数据

    以10000条数据为例。HashMap用去约13.2M内存,SparseArray共用去 8.626M内存。

  2. 数据插入

    在正序插入数据时候,SparseArray比HashMap要快一些;HashMap无论是倒序还是正序开销差点儿是一样的。可是SparseArray的倒序插入要比正序插入要慢很多,为什么呢?

    原因:SparseArray在检索数据的时候使用的是二分查找,所以每次插入新数据的时候SparseArray都须要又一次排序,所以逆序是最差情况。

  3. 数据检索

    SparseArray中存在须要检索的下标时。SparseArray的性能略胜一筹可是当检索的下标比較离散时,SparseArray须要使用多次二分检索,性能显然比hash检索方式要慢一些了。

  4. 接口实现

    HashMap实现了Cloneable, Serializable SparseArray 实现了Cloneable 接口,也就是说SparseArray 是不支持序列化的。

3.代码解析-功法传授(Q增 W删 E改 R查)

先天优越之—>初始技能

 /**
  * Creates a new SparseArray containing no mappings.
  * 创建默认容器大小为10的SparseArray
  */
public SparseArray() {
    this(10);
}

public SparseArray(int initialCapacity) {...}

如虎添翼之 —>

/**
 * Adds a mapping from the specified key to the specified value,
 * replacing the previous mapping from the specified key if there
 * was one.
通过指定的key和value加入一个键值对,假设这个位置已经存在一个了,则替换掉
 */
public void put(int key, E value) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

    if (i >= 0) {
        mValues[i] = value;
    } else {
        i = ~i;

        if (i < mSize && mValues[i] == DELETED) {
            mKeys[i] = key;
            mValues[i] = value;
            return;
        }

        if (mGarbage && mSize >= mKeys.length) {
            gc();

            // Search again because indices may have changed.
            i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
        }

        mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
        mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
        mSize++;
    }
}

/**
 * Puts a key/value pair into the array, optimizing for the case where
 * the key is greater than all existing keys in the array.
通过指定的key和value加入一个键值对,在原有的基础上添加
 */
public void append(int key, E value) {
    if (mSize != 0 && key <= mKeys[mSize - 1]) {
        put(key, value);
        return;
    }

    if (mGarbage && mSize >= mKeys.length) {
        gc();
    }

    mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
    mValues = GrowingArrayUtils.append(mValues, mSize, value);
    mSize++;
}

割袍断义之 —>

/**
 * Removes the mapping from the specified key, if there was any.
 * 从键值对中删除指定的key
 */
public void delete(int key) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

    if (i >= 0) {
        if (mValues[i] != DELETED) {
            mValues[i] = DELETED;
            mGarbage = true;
        }
    }
}

/**
 * @hide
 * 隐藏方法
 * Removes the mapping from the specified key, if there was any, returning the old value.
 * 从键值对中删除指定的key,假设在不论什么地方还实用到,会返回旧值。

*/
public E removeReturnOld(int key) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

    if (i >= 0) {
        if (mValues[i] != DELETED) {
            final E old = (E) mValues[i];
            mValues[i] = DELETED;
            mGarbage = true;
            return old;
        }
    }
    return null;
}

/**
 * Alias for {@link #delete(int)}. (注意:这里是调用的delete 的方法,所以remove方法和 delete效果是一样的)
 */
public void remove(int key) {
    delete(key);
}
/**
 * Removes the mapping at the specified index.
 * 删除指定index(注意这里不是删除指定的key,可是事实上我们能够发现这个实现是和delete方法是一样的)
 */
public void removeAt(int index) {
    if (mValues[index] != DELETED) {
        mValues[index] = DELETED;
        mGarbage = true;
    }
}
/**
 * Remove a range of mappings as a batch.
 * 删除一组数据
 * @param index Index to begin at index  開始删除的位置
 * @param size Number of mappings to remove  删除的长度
 */
public void removeAtRange(int index, int size) {
    final int end = Math.min(mSize, index + size);
    for (int i = index; i < end; i++) {
        removeAt(i);
    }
}

偷梁换柱之—>

/**
 * Returns the index for which {@link #keyAt} would return the
 * specified key, or a negative number if the specified
 * key is not mapped.
 * 返回key 相应的index,假设指定的key没有找到则返回一个负数
 */
public int indexOfKey(int key) {
    if (mGarbage) {
        gc();
    }

    return ContainerHelpers.binarySearch(mKeys, mSize, key);
}

/**
 * Given an index in the range <code>0...size()-1</code>, sets a new
 * value for the <code>index</code>th key-value mapping that this
 * SparseArray stores.
改动指定index 下相应的value 值(注意这里 第一个參数是index,不是 key。能够配合indexOfKey(int key)先获得key所在的index)
 */
public void setValueAt(int index, E value) {
    if (mGarbage) {
        gc();
    }

    mValues[index] = value;
}

按图索骥之—>

/**
 * Gets the Object mapped from the specified key, or <code>null</code>
 * if no such mapping has been made.
通过指定的key获取相应的Object,假设没有找到相应的键值对,会默认返回null
 */
public E get(int key) {
    return get(key, null);
}

/**
 1. Gets the Object mapped from the specified key, or the specified Object
 2. if no such mapping has been made.
 3. 通过运行的key获取相应的Object,,假设没有找到相应的键值对。会返回设置的默认值
 */
@SuppressWarnings("unchecked")
public E get(int key, E valueIfKeyNotFound) {
    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);

    if (i < 0 || mValues[i] == DELETED) {
        return valueIfKeyNotFound;
    } else {
        return (E) mValues[i];
    }
}

寻名剑(键) —>获得key

/**
获得指定index 下的key
 * */
public int keyAt(int index) {
    if (mGarbage) {
        gc();
    }
    return mKeys[index];
}

剑鞘—>获得value

/**
 * 获得指定index 相应下的value
 * */

public E valueAt(int index) {
    if (mGarbage) {
        gc();
    }

    return (E) mValues[index];
}

一个萝卜一个坑 —>*依据value 获得index*

/**
 * 依据指定的value 获得所在的index,假设没有 则返回-1
 */

public int indexOfValue(E value) {
    if (mGarbage) {
        gc();
    }

    for (int i = 0; i < mSize; i++)
        if (mValues[i] == value)
            return i;

    return -1;
}

终极必杀之受死吧妖孽,打回原形 —>clear()

/**
 * Removes all key-value mappings from this SparseArray.
 * 从SparseArray 中移除所有的key-value 键值对
 */
public void clear() {
    int n = mSize;
    Object[] values = mValues;

    for (int i = 0; i < n; i++) {
        values[i] = null;
    }

    mSize = 0;
    mGarbage = false;
}
时间: 2024-10-06 02:54:44

SparseArray具体解释,我说SparseArray,你说要!的相关文章

Android源码分析之SparseArray

本来接下来应该分析MessageQueue了,可是我这几天正好在实际开发中又再次用到了SparseArray(之前有用到过一次,那次只是 大概浏览了下源码,没做深入研究),于是在兴趣的推动下,花了些时间深入研究了下,趁着记忆还是新鲜的,就先在这里分析了. MessageQueue的分析应该会在本周末给出. 和以往一样,首先我们来看看关键字段和ctor: private static final Object DELETED = new Object(); private boolean mGar

Android内存优化(使用SparseArray和ArrayMap代替HashMap)

转载自:http://blog.csdn.net/u010687392/article/details/47809295 在Android开发时,我们使用的大部分都是Java的api,比如HashMap这个api,使用率非常高,但是对于Android这种对内存非常敏感的移动平台,很多时候使用一些java的api并不能达到更好的性能,相反反而更消耗内存,所以针对Android这种移动平台,也推出了更符合自己的api,比如SparseArray.ArrayMap用来代替HashMap在有些情况下能带

intellij老是警告的SparseArray是什么 - HashMap的替代者

序言 身为一个有代码洁癖的程序员,在写Android应用的时候,我总是会去注意 代码规范(Google Android Guideline) 能一行搞定的代码,绝不写两行 决不让编译器(intellij, as)右边滚动条有黄色 不重复自己 当然了,实际开发中,编译器报的warning有些不太好避免,比如有些空指针,编译器从android源码来看,觉得不会出现空指针,但是实际情况下-.你懂得,部分rom手贱改坏了源码,结果就crash了,所以我们能做的,就是尽量减少warning. 扯了这么多,

Android SparseArray源码分析

前言 昨晚想在Android应用中增加一个int映射到String的字典表,使用HashMap实现的时候,Eclipse给出了一个警告,昨晚项目上线紧张,我直接给忽略了,今天看了一下具体的Eclipse提示如下: Use new SparseArray<String> (...) instead for better performance 这个警告的意思是使用SparseArray来替代,以获取更好的性能. 源码 因为SparseArray整体代码比较简单,先把源码展示出来,然后再分析为什么

数据结构HashMap(Android SparseArray 和ArrayMap)

HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置,我们总是可以通过key快速地存.取value. HashMap HashMap.java源码分析:三个构造函数:HashMap():默认初始容量capacity(16),默认加载因子factor(0.75)HashMap(int initialCap

Android性能专项测试之耗电量统计API

参考文章:Android应用的耗电量统计 深入浅出Android App耗电量统计 Battery stats - CPU total vs CPU foreground 深入浅出 Android App 耗电量统计 浅析Wakelock机制与Android电源管理 耗电量API Android系统中很早就有耗电量的API,只不过一直都是隐藏的,Android系统的设置-电池功能就是调用的这个API,该API的核心部分是调用了com.android.internal.os.BatteryStat

Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复

Android中的状态保存和恢复 Android中的状态保存和恢复, 包括Activity和Fragment以及其中View的状态处理. Activity的状态除了其中的View和Fragment的状态之外, 还需要用户手动保存一些成员变量. Fragment的状态有它自己的实例状态和其中的View状态, 因为其生命周期的灵活性和实际需要的不同, 情况会多一些. 根据源码, 列出了Fragment中实例状态和View状态保存和恢复的几个入口, 便于分析查看. 最后专门讲了WebView状态保存和

5.5 数据结构和算法

1.数据的逻辑结构: 1)线性结构:(只有一个开始结点和一个终端结点) 2)非线性结构:(一个结点有多个前驱结点和后继结点) A: 集合:(元素之间的关系较为松散)                B: 线性结构:(元素之间存在严格的一对一的关系) C: 树形结构:(元素之将存在严格的一对多关系)      D: 网状结构: (元素之间存在多对多关系) 2.数据的存储结构: A:顺序结构   B:连接结构    C:索引结构    D:散列结构 对JAVA的集合的理解是相对于数组.数组是大小固定的

解决RecyclerView无法onItemClick问题

对于RecyclerView的使用,大家可以查看将替代ListView的RecyclerView 的使用(一),单单从代码结构来说RecyclerView确实比ListView优化了很多,也简化了我们编写代码量,但是有一个问题会导致开发者不会去用它,更比说替换ListView了,我不知道使用过RecyclerView的人有没有进一步查看,RecyclerView没有提供Item的点击事件,我们使用列表不仅仅为了显示数据,同时也可以能会交互,所以RecyclerView这个问题导致基本没有人用它,