一、简介
1、适用于键为int的map数据结构类型
2、适用于小规模数据存储
3、内部使用二分查找进行数据查询,查询效率高
4、不需要单独开辟内存来映射对象,节约内存
二、基本使用
1、创建mSparseArray全局变量
val mSparseArray=SparseArray<String>()
2、添加操作
添加过程中通过添加两个键重复,值不重复的两个数据,发现键重复,值覆盖和hashMap相同
btn_sparse_array_put.setOnClickListener {
mSparseArray.put(1,"Red")
mSparseArray.put(2,"Green")
mSparseArray.put(3,"Blue")
mSparseArray.put(4,"Yellow")
mSparseArray.put(4,"Gray")
Log.e("peter", "size is-> ${mSparseArray.size()} sparseArray is ->$mSparseArray")
}
结果
03-19 03:47:44.396 29513-29513/com.gee.peter.testapplication E/peter: size is-> 4 sparseArray is ->{1=Red, 2=Green, 3=Blue, 4=Gray}
3、删除操作
删除操作,当删除的键不存在时,不执行删除操作,不会报错
btn_sparse_array_delete.setOnClickListener {
if (mSparseArray.size()==0)return@setOnClickListener
mSparseArray.delete(0)// 根据键删除
mSparseArray.delete(1)// 根据键删除
mSparseArray.removeAt(2)// 删除索引为2的元素
Log.e("peter","size is -> ${mSparseArray.size()} sparseArray is->$mSparseArray")
}
结果:
删除了键为1和索引为2的元素
size is -> 2 sparseArray is->{2=Green, 4=Gray}
4、修改操作
修改索引为0的元素值
btn_sparse_array_modify.setOnClickListener {
mSparseArray.setValueAt(0,"modifyColor")
Log.e("peter","modifyColor is -> ${mSparseArray.valueAt(0)} sparseArray is->$mSparseArray")
}
结果:
modifyColor is -> modifyColor sparseArray is->{2=modifyColor, 4=Gray}
5、查询操作
通过添加和查询两个操作来实现
btn_sparse_array_search.setOnClickListener {
mSparseArray.put(1,"Red")
mSparseArray.put(2,"Green")
mSparseArray.put(3,"Blue")
mSparseArray.put(4,"Yellow")
mSparseArray.put(4,"Gray")
for (i in 0..mSparseArray.size()){
Log.e("peter","mSparseArray index -> $i value is ->${mSparseArray.valueAt(i)}")
}
结果:
mSparseArray index -> 0 value is ->Red
mSparseArray index -> 1 value is ->Green
mSparseArray index -> 2 value is ->Blue
mSparseArray index -> 3 value is ->Gray
mSparseArray index -> 4 value is ->null
通过结果可见:虽然我们插入的键为4的数据已经在sparseArray中存在了,但是执行的操作是将键重复的进行值覆盖,但是仍然并未立即删除该索引,而是将其标记为删除状态,方便以后复用
三、主要方法源码解析
主要方法:
1、put方法
/**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
*/
public void put(int key, E value) {
// 利用二分查找查询是否有该键
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value; // 存在该键直接获取该元素修改对应的值
} else {
i = ~i; // 否则i<0,执行非操作
if (i < mSize && mValues[i] == DELETED) {
// 如果已被标记为删除状态了并且i<size,表明array的容量足够,并且之前已经申请了存储空间,直接进行赋值即可
mKeys[i] = key;
mValues[i] = value;
return;
}
// 如果需要执行垃圾回收,并且array的容量大于mKeys的长度,也就是说array申请了多余的空间
if (mGarbage && mSize >= mKeys.length) {
gc(); // 执行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++; // mSize增加
}
}
2、删除数据
remove(int key)通过键删除数据
/**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
delete(key);
}
/**
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
// 查询该key在mKsys中对应的索引
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
// 标记为DELETED,将垃圾回收标记改为true
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}
removeAt(int index) 通过索引删除
public void removeAt(int index) {
// 标记为DELETED,将垃圾回收标记修改为true
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
removeAtRange(int index, int size) 删除从指定开始的几个元素
/**
* Remove a range of mappings as a batch.
*
* @param index Index to begin at
* @param size Number of mappings to remove
*
* <p>For indices outside of the range <code>0...size()-1</code>,
* the behavior is undefined.</p>
*/
public void removeAtRange(int index, int size) {
// 防止索引越界,取mSize与index+size的最小值,机智
final int end = Math.min(mSize, index + size);
for (int i = index; i < end; i++) {
removeAt(i);
}
}
3、修改数据 setValueAt(int index, E value)
/**
* 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.
*
* <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined.</p>
*/
public void setValueAt(int index, E value) {
// 先进行垃圾回收,及时释放内存
if (mGarbage) {
gc();
}
// 赋值
mValues[index] = value;
}
4、查询操作
get(int key) 通过键查询值 默认值为null
/**
* Gets the Object mapped from the specified key, or <code>null</code>
* if no such mapping has been made.
*/
public E get(int key) {
return get(key, null);
}
/**
* Gets the Object mapped from the specified key, or the specified Object
* if no such mapping has been made.
*/
@SuppressWarnings("unchecked")
public E get(int key, E valueIfKeyNotFound) {
// 通过二分查找查询索引
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
// 未查询到返回未查询到的值,默认值为null
return valueIfKeyNotFound;
} else {
// 返回该键对应的值
return (E) mValues[i];
}
}
keyAt(int index) 通过索引获取键值
public int keyAt(int index) {
if (mGarbage) {
gc();
}
// 获取keys中index对应的key值
return mKeys[index];
}
valueAt(int index) 通过索引获取值
@SuppressWarnings("unchecked")
public E valueAt(int index) {
if (mGarbage) {
gc();
}
// 获取该index在mValues中对应的value值
return (E) mValues[index];
}
indexOfKey(int key) 通过key查询对应的索引
public int indexOfKey(int key) {
if (mGarbage) {
gc();
}
// 使用二分查找法,通过key查询对应索引
return ContainerHelpers.binarySearch(mKeys, mSize, key);
}
indexOfValue(E value) 通过索引查询对应的value值
public int indexOfValue(E value) {
if (mGarbage) {
gc();
}
// 通过遍历进行查询索引
for (int i = 0; i < mSize; i++) {
// 可以用来比较基本数据类型和引用数据类型
if (mValues[i] == value) {
return i;
}
}
// 未查询到返回-1
return -1;
}
indexOfValueByValue(E value) 通过equals方法判断获取index
public int indexOfValueByValue(E value) {
if (mGarbage) {
gc();
}
for (int i = 0; i < mSize; i++) {
if (value == null) {
if (mValues[i] == null) {
return i;
}
} else {
// 使用equals比较如果对应的类未重写equals方法,则用于比较引用数据类型的地址值
if (value.equals(mValues[i])) {
return i;
}
}
}
return -1;
}
5、gc() 垃圾回收方法
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
// 获取values对应的数据
Object val = values[i];
if (val != DELETED) {
if (i != o) { // 只有当i!=o时触发
// 将value向前移动一位,将values[i]置为null
keys[o] = keys[i]; // 键移动 将索引 i 处的 key 赋值给 o 处的key
values[o] = val; // 值移动 将索引 i 处的 value 赋值给 o 处的value
values[i] = null;
}
o++;
}
}
mGarbage = false;
mSize = o;
// Log.e("SparseArray", "gc end with " + mSize);
}
**6、使用的最多的二分查找 binarySearch(int[] array, int size, int value) **
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
// 通过位右移获取mid
final int mid = (lo + hi) >>> 1;
// 通过获取获取mid对应的索引值
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1; // 改变lo的index
} else if (midVal > value) {
hi = mid - 1; // 改变hi的index
} else {
return mid; // value found
}
}
return ~lo; // value not present
}