Android替代HashMap的SparseArray

一、概述

最近在项目中看到了SparseArray,好奇研究了下。 SparseArray是Android框架独有的类,在标准的JDK中不存在这个类。它要比 HashMap 节省内存,某些情况下比HashMap性能更好,按照官方问答的解释,主要是因为SparseArray不需要对key和value进行auto-boxing(将原始类型封装为对象类型,比如把int类型封装成Integer类型),结构比HashMap简单(SparseArray内部主要使用两个一维数组来保存数据,一个用来存key,一个用来存value)不需要额外的额外的数据结构(主要是针对HashMap中的HashMapEntry而言的)。

二、详解

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

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

\

在此数组中,共有63个空间,但却只使用了5个元素,造成58个元素空间的浪费。以下我们就使用稀疏数组重新来定义这个数组:
\

三、代码

下面分析下SparseArray到底是什么东西?
 SparseArray<String> sa = new SparseArray<>();
 sa.put(1,"world");
进到put源码里看下
public void put(int key, E value) {
        int i = ContainerHelpers.<span style="color:#ff0000;">binarySearch</span>(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++;
        }
    }
binarySearch很明显是二分查找,首先put先通过二分查找,看目标在不在我们的sparseArray当中,如果已经存在了的话,那么直接替换掉旧的,如果不存在,就直接保存,我们再看下get方法:
public E get(int key, E <span style="color:#ff0000;">valueIfKeyNotFound</span>) {
        int i = ContainerHelpers.<span style="color:#ff0000;">binarySearch</span>(mKeys, mSize, key);

        if (i < 0 || mValues[i] == DELETED) {
            return valueIfKeyNotFound;
        } else {
            return (E) mValues[i];
        }
    }
</pre>valueIfKeyNotFound是取不到结果时候的默认值,首先get方法也是通过binarySearch进行查找的,那么问题来了,对于大规模数量的put,每一次put都要进行按序排列,如果key值恰恰是按照从小到大的顺序排列的,那么SparseArray无非是最好的存储方式,如果key值按照从大到小的方式排列,那么就会造成每一次浪费大量时间进行数据重排。</div><div><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 10px; font-size: 14px; line-height: 26px; color: rgb(51, 51, 51); font-family: 微软雅黑, Tahoma, Verdana, 宋体;">ArrayMap</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 10px; font-size: 14px; line-height: 26px; color: rgb(51, 51, 51); font-family: 微软雅黑, Tahoma, Verdana, 宋体;">这个api的资料在网上可以说几乎没有,然并卵,只能看文档了 ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录key的hash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作,所以,应用场景和SparseArray的一样,如果在数据量比较大的情况下,那么它的性能将退化至少50%。</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 10px; padding-bottom: 10px; font-size: 14px; line-height: 26px; color: rgb(51, 51, 51); font-family: 微软雅黑, Tahoma, Verdana, 宋体;">添加数据</p><pre style="color: rgb(51, 51, 51); overflow-y: auto; border: 1px solid rgb(204, 204, 204); line-height: 25px; border-radius: 3px; padding: 10px; background-color: rgb(245, 245, 245);"><code>public V put(K key, V value)</code>

获取数据

public V get(Objectkey)

删除数据

public V remove(Objectkey)

特有方法

它和SparseArray一样同样也有两个更方便的获取数据方法:

public K keyAt(int index)
public V valueAt(int index)

ArrayMap应用场景

  • 数据量不大,最好在千级以内
  • 数据结构类型为Map类型
ArrayMap<Key,Value> arrayMap = new ArrayMap<>();

总结

SparseArray和ArrayMap都差不多,使用哪个呢? 
假设数据量都在千级以内的情况下:

1、如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用

2、如果key类型为其它的类型,则使用ArrayMap


四、总结

虽然从时间上考虑,SparseArray不一定是最好的选择,但是从内存开销上来看,SparseArray采用稀疏数组的方式还是节约了很大的内存开销,所以我们还是采取Android官方的建议。今后多使用SparseArray进行编程。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值