android 数组不限制,Android中的稀疏数组:SparseArray

Android中的稀疏数组:SparseArray

30 Mar 2014

在一般情况下,使用 HashMap,如果 K 整数类型的话,使用 SparseArray 效率会更高。

和HashMap类似,SparseArray 建立整数索引和对象的关系。和简单的对象数组相比,SparseArray 允许索引之间有间隔。

SparseArray 支持和 HashMap 类似的 put 和 get 方法。在其内部,维护着两个数组,一个用于存储索引,一个用于存储对象。

public class SparseArray implements Cloneable {

private int[] mKeys;

private Object[] mValues;

private int mSize;

整数索引被从小到大映射到 mKeys 数组中。

索引的映射

在计算整数索引映射到数组中的位置的时候,用了一个改造过的的二分搜索算法:

这个算法输入的参数是:要搜索的数组a,搜索的起始位置 start, 搜索的长度 len, 要检索的关键字 key,如下:

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;

}

这是一个巧妙设计的算法,如果输入的 key 在区间内则返回等于关键字或者最小的大于关键字的索引。

如果关键字不在区间内,则将区间首个索引或者区间最后一个索引加1取反码,非负数的反码都是负数,因为符号位被取反了。

有一个数组: int[] a = new int[] {2, 5, 8, 0, 0}; 结构如下:

0 1 2 3 4

+---+---+---+---+---+

| 2 | 5 | 8 | | |

+---+---+---+---+---+

对于 start = 0, len = 3, 对于不同的关键字:

key

return1

-1

在区间范围最左边,返回区间最首个索引取反

2

0

关键字存在,返回对应的索引

4

1

返回最小的比关键字大的值的对应的索引, 即 5 的索引

5

1

关键字存在,返回对应的索引

9

-4

在区间最右边,区间最右索引加1取反 ~(2+1)

put 过程

put 的过程分为以下几步:

计算索引映射。

如果在在区间内有对应槽位,设置值,返回。

如有必要,进行扩容。

容量以类似2的指数次幂增长。对象引用和和整数都占用4个字节,数组本身还需要占用3个字节。

为了内存4字节对齐,数组大小应该是:2^n - 3(n >=2)。

如有必要,移动区段

如果计算出的映射索引,在现有对象的位置上,需要移动区段。

对于上述数组,如果要插入 4:计算得的索引为 1,需要将索引位置开始的所有元素后移:

0 1 2 3 4

+---+---+---+---+---+

| 2 | | 5 | 8 | |

+---+---+---+---+---+

最后,设置值,将数据长度加 1。

主要代码如下,省略了部分细节:

public void put(int key, E value) {

// 1. 计算索引

int i = binarySearch(mKeys, 0, mSize, key);

// 2. key已经有对应槽位,更新值

if (i >= 0) {

mValues[i] = value;

} else {

i = ~i;

// 3. 扩容

if (mSize >= mKeys.length) {

}

// 4. 移动区段

if (mSize - i != 0) {

// Log.e("SparseArray", "move " + (mSize - i));

System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);

System.arraycopy(mValues, i, mValues, i + 1, mSize - i);

}

// 4. 设置值,长度加 1

mKeys[i] = key;

mValues[i] = value;

mSize++;

}

}

get 和遍历

如果索引不存在,indexOfKey(int key),将会返回负数值。

遍历需要获取数组的总的对象大小,然后用 keyAt(int index) 获取索引或者 valueAt(int index) 获取值。

int key = 0;

for(int i = 0; i < sparseArray.size(); i++) {

key = sparseArray.keyAt(i);

Object obj = sparseArray.valueAt(key);

}

效率的提升和使用限制

稀疏数组的使用,对于索引是整数的情景,有时能带来一些效率的提升。

减少了 hashCode 时间消耗

减小了所使用的内存大小。

和 SparseArray 类似的,有 SparseBooleanArray, SparseIntArray。前者,减少了存储对象占用的空间,后者减少了类型转换。

但在所管理的对象数量很大时,效率却反而有可能更低:

在插入的时候,有可能导致大段数组的复制;

在删除之后,也有可能导致数组的大段元素被按个移动(不是复制数组,而是一个一个单独移动);

索引的映射,采用了二分查找,时间复杂度为 O(logn)。

写完这篇文章,搜索了一下 相关的内容 发现中文的文章,大部分都是重复的,几个原创的也有一些错误。特强调如下:

SparseArray 是针对HashMap做的优化。

HashMap 内部的存储结构,导致一些内存的浪费。

在刚扩容完,SparseArray 和 HashMap 都会存在一些没被利用的内存。

SparseArray 并不是任何时候都会更快,有时反而会更慢

Follow Me on GitHub

comments powered by Disqus

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值