【Android】Android中的数据结构:SparseArray与ArrayMap

SparseArray和ArrayMap是Android中的特有集合类数据结构。

一、SparseArray

SparseArray直译为稀疏数组,本质上是通过数组实现的、key为整型的键值对集合。刷过算法题的应该对类似的数据结构很熟悉。

1. SparseArray的用法

SparseArray的一般使用类似于key为整型的HashMap。如下:

        SparseArray<String> arr = new SparseArray<>();
        arr.put(1, "one");
        arr.put(100, "one hundred");
        arr.put(10000, "ten thousand");
        arr.remove(100);

另外:

  • 使用append方法与put方法类似的添加元素;不过如果待添加的key大于当前所有元素,则直接加在末尾,少了二分查找的这一步,所以比put方法效率要高。
  • 使用indexOfKey(key)indexOfValue(value)可以查找对应的key或者value在数组中的序号。
  • 使用keyAt(index)valueAt(index)方法可以查找对应序号的key或者value。

2. SparseArray的实现原理

SparseArray的实现原理是通过整型数组mKeys和值数组mValues建立的一一对应关系,通过mSize属性来标记其键值对的数量(mSize对应的键值对是真实存在于集合中的元素,不包括已经删除的)。

mKeys数组是有序递增的,这样有助于进行二分查找。当往SparseArray中添加键值对时,会通过二分查找找到相应的位置并插入。当进行删除时,同样会通过二分查找找到相应的键值对,然后将mValues数组中对应的值赋为DELETEDDELETED是一个常量对象,无实际含义,只作为该键值对已经被删除的标记)。

查找

当查找元素时,会使用二分查找,所以SparseArray的查找时间复杂度是O(logN)。

删除

当删除元素时,会先查找到该元素;这时并不会立即把这个元素删除,而只是标记其值为DELETED,这是SparseArray的延时删除机制,可以提高执行效率。

添加

在使用put方法添加元素时,会先查找元素,如果找到对应的key,那么就直接修改对应的值;如果没有找到对应的key,那么就会添加新值。
在添加新值之前,会检查二分查找最接近的那个元素,如果该元素值为DELETED,则直接将该元素替换为待添加的元素;否则,在此处插入新元素。
在插入元素之前,如果键值对数量mSize已经大于等于mKeys数组长度,SparseArray会进行垃圾回收gc()。这里的垃圾回收不同于JVM的垃圾回收,而是删除所有值为DELETED的键值对,并将其后面的元素依次靠拢。在垃圾回收之后,如果mSize仍旧大于等于mKeys的长度,数组会进行扩容
特别地,当使用append方法添加数据,且添加的key大于当前数组中所有元素时,则会直接插入在数组最后;其他情况下append会直接调用put方法。所以,使用SparseArray添加元素时,优先使用append方法而不是put

如果没有特别指定,SparseArray的初始大小为10,每次扩容之后大小为之前的2倍。

3. 时间复杂度分析

查找:当查找元素时,会使用二分查找,所以SparseArray的查找时间复杂度是O(logN)。

删除:当删除元素时,会先查找该元素,然后标记值为DELETED,所以删除的时间复杂度也是O(logN)。

添加:当使用put方法添加元素时,会先查找元素,如果找到对应的key,或者最接近的元素value是DELETED,那么就直接赋值,这种情况的时间复杂度是O(logN)。
如果触发了垃圾回收或者扩容,那么时间复杂度就是O(N),这是最坏时间复杂度。
当使用append方法添加数据,且数据为正序插入时,时间复杂度为O(1),其他情况下时间复杂度同put

4. 小结

  • SparseArray用法类似于HashMap,但key恒定为整型。
  • SparseArray通过mKeysmValues两个相等长度的数组来实现键值对的一一对应,其中mKeys是升序的。
  • 时间复杂度分析:
    因为查找元素使用二分法,所以SparseArray删改查平均时间复杂度均为O(logN);添加元素由于垃圾回收gc()和插入元素insert都可能会进行数组复制的原因,时间复杂度最好是O(logN),最坏是O(N)。
  • 和HashMap相比,SparseArray的优势是:
    1、使用int[]保存key,不用拆装箱,也没有复杂的运算,在数据量小时实际效率比较高;
    2、除了数组没有额外的数据结构,节省空间;
    劣势是:
    1、在数据量较大以后,效率很低;
  • SparseArray可以看做是用时间换空间的数据结构。

二、ArrayMap

ArrayMap是将key和value保存在同一个数组中的集合类数据结构。

1. ArrayMap的用法

ArrayMap的用法也类似于SparseArray,就不多做赘述。

2. ArrayMap的原理

不同于SparseArray,ArrayMap将key和value储存在同一个数组mArray中,并使用升序数组mHashes保存了各个元素的哈希值。
其中,第i个元素的哈希值为mHashes[i],key为mArray[2i],value为mArray[2i+1]。元素的总数量为mSize

查找

ArrayMap的核心在于查找,也就是如何找到对应的键值对在数组中的存放位置,对应的是ArrayMap的indexOf()方法。
由于mHashes是升序数组,所以先计算对应key的哈希值,通过二分法查找到mHashes中对应的哈希值。如果查找到的key与待查找的key不同,则在mArray中以当前key为中心,往左右依次匹配,直到查找到对应的key。

插入

在ArrayMap中插入新元素,首先检查容量是否达到上限,如果达到上限会进行扩容;扩容后的大小为原大小的1.5倍。
然后,通过System.arraycopy将待插入元素右侧的所有元素往右移动(mHashes移动一位,mArray移动两位),将元素插入。
插入后,mSize加一。

删除

删除存在的元素,首先找到该元素,然后通过System.arraycopy将数组右边的所有元素往左移动(mHashes移动一位,mArray移动两位)。如果删除元素之后,实际元素个数mSize小于阈值(mHashes数组长度的1/3),那么会进行减容;减容后的大小为mSize的1.5倍。

3. 时间复杂度分析

查找
查找元素要经历两次:第一次是通过二分查找在mHashes数组中找到对应的哈希值,第二次是通过第一次查找的结果在mArray中找到对应的key。
第一次查找时间复杂度为O(logN),第二次查找根据哈希碰撞的情况,最好为O(1),最坏为O(n)。所以总的时间复杂度最好为O(logN),最坏为O(N)。因为实际情况下哈希碰撞不会经常出现,所以平均时间复杂度可以近似看做O(logN)。

插入
因为插入的过程会进行数组的复制,所以理论时间复杂度为O(N)。

删除
同插入。

4. 小结

  • ArrayMap通过升序数组mHashes数组保存了元素的哈希值,mArray数组保存了元素的key-value。其中,对于第i个元素,其哈希值为mHashes[i],key为mArray[2i],value为mArray[2i+1]
  • ArrayMap在添加元素和删除元素时,都会进行数组的复制。
  • 与SparseArray类似,ArrayMap相对于HashMap节约了空间,但是降低了效率,是用时间换空间的数据结构。

三、总结

  1. SparseArray和ArrayMap都是通过数组实现的数据结构,二者使用方式类似。其中SparseArray是通过两个数组分别保存key和value,且key只能是整型;而ArrayMap是通过一个数组保存哈希值,另一个数组同时保存key和value。二者查找元素都是通过二分查找实现。

  2. SparseArray和ArrayMap与HashMap相比,都是用时间换空间的数据结构,可以节约运行内存,但是会降低效率。如果应用更追求占用较小的内存,且数据量小,则使用SparseArray/ArrayMap;追求更快的运行速度,或数据量大,则使用HashMap。

  3. SparseArray有延迟删除的机制,可以有效的提高效率;ArrayMap即时删除,且有减容机制,可以有效的节省空间。

  4. 当使用SparseArray添加数据时,优先使用append方法,而不是put方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容