HashMap can be replaced with SparseArray--Android应用性能优化之使用SparseArray替代HashMap

HashMap是java里比较常用的一个集合类,我比较习惯用来缓存一些处理后的结果。最近在做一个Android项目,在代码中定义这样一个变量,实例化时,Eclipse却给出了一个 performance 警告。



意思就是说用SparseArray <E> 来替代,以获取更好性能。老实说,对SparseArray并不熟悉,第一感觉应该是Android提供的一个类。按住Ctrl点击进入SparseArray的源码,果不其然,确定是Android提供的一个工具类。


单纯从字面上来理解,SparseArray指的是稀疏数组(Sparse array) ,所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容

假设有一个9*7的数组,其内容如下:



其中在稀疏数组中第一部分所记录的是原数组的列数和行数以及元素使用的个数、第二部分所记录的是原数组中元素的位置和内容。经过压缩之后,原来需要声明大小为63的数组,而使用压缩后,只需要声明大小为6*3的数组,仅需18个存储空间。

继续阅读SparseArray的源码,从构造方法我们可以看出,它和一般的List一样,可以预先设置容器大小,默认的大小是10:

    public SparseArray() {
        this(10);
    }
    public SparseArray(int initialCapacity) {
        initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
        mKeys = new int[initialCapacity];
        mValues = new Object[initialCapacity];
        mSize = 0;
    }

再来看看它对数据的“增删改查”。

(1)它有两个方法可以添加键值对:

 public void put(int key, E value) {}
 public void append(int key, E value){}

(2)有四个方法可以执行删除操作:
 public void delete(int key) {}
 public void remove(int key) {} //直接调用的delete(int key)
 public void removeAt(int index){}
 public void clear(){}

(3)修改操作
修改数据起初以为只有setValueAt(int index, E value)可以修改数据,但后来发现put(int key, E value)也可以修改数据,我们查看put(int key, E value)的源码可知,在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。
public void put(int key, E value) {
        int i = binarySearch(mKeys, 0, 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 = ~binarySearch(mKeys, 0, mSize, key);
            }
            …………

所以,修改数据实际也有两种方法:
 public void put(int key, E value)
 public void setValueAt(int index, E value)

(4)最后再来看看如何查找数据。有两个方法可以查询取值:
 public E get(int key)
 public E get(int key, E valueIfKeyNotFound)

其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。

查看第几个位置的键:public int keyAt(int index)
有一点需要注意的是,查看键所在位置,由于是采用二分法查找键的位置,所以找不到时返回小于0的数值,而不是返回-1。返回的负值是表示它在找不到时所在的位置。

查看第几个位置的值:
public E valueAt(int index)
查看值所在位置,没有的话返回-1:
public int indexOfValue(E value)
最后,发现其核心就是折半查找函数(binarySearch),算法设计的很不错。

private static int binarySearch(int[] a, int start, int len, int key) {
        int high = start + len, low = start - 1, guess;
        while (high - low >  1) {
            guess = (high + low) / 2;
            if (a[guess]  < key)
                low = guess;
            else
                high = guess;
        }
        if (high == start + len)
            return ~(start + len);
        else if (a[high] == key)
            return high;
        else
            return ~high;
    }

相应的也有SparseBooleanArray,用来取代HashMap <Integer, Boolean> ,SparseIntArray用来取代HashMap <Integer, Integer> ,大家有兴趣的可以研究。

总结:
SparseArray是android里为<Interger,Object> 这样的Hashmap而专门写的类,目的是提高效率,其核心是折半查找函数(binarySearch)。在Android中,当我们需要定义
HashMap <Integer, E> hashMap = new HashMap <Integer, E> ();
时,我们可以使用如下的方式来取得更好的性能.
SparseArray <E> sparseArray = new SparseArray <E> ();

原文URL: https://liuzhichao.com/p/832.html

=====================

SparseArray

extends  Object
implements  Cloneable
java.lang.Object
   ↳ android.util.SparseArray<E>

Class Overview


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.

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

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.

It is possible to iterate over the items in this container using keyAt(int) and valueAt(int). Iterating over the keys using keyAt(int) with 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).


摘自:http://developer.android.com/reference/android/util/SparseArray.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值