SparseArray和ArrayMap相关源码解析
2017-04-28 17:28
239 查看
内存优化:Android API当中提供了一些优化过后的数据集合工具类,如SparseArray,SparseBooleanArray,以及LongSparseArray等,使用这些API可以让我们的程序更加高效。本文整理SparseArray和ArrayMap相关内部实现。
一、SparseArray
SparseArray的特点避免了key为int值时的自动装箱,优化了性能。
1、构造函数
public SparseArray() {
this(10);
}预先设置的容器大小为10。
2、变量
private int[] mKeys;
private Object[] mValues;两个数组进行存储,一个存储key,一个存储value。
3、put方法
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(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++;
}
}put方法中采用了二分查找比较元素key值的大小,并进行排序。
#ContainerHelpers 二分查找
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}4、get方法
public E get(int key) {
return get(key, null);
}
5、delete方法
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}删除元素同样使用了二分查找。
总结:增加元素、删除元素、查找元素都使用了二分查找。所以SparseArray适用于数据量不大,并且key为int值的时候。
同样的,SparseBooleanArray适用于key为boolean的情况,内部实现相似。
二、ArrayMap
public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V>ArrayMap继承SimpleArrayMap,实现了Map接口。以key-value的形式存储数据,相比HashMap会占用更少的内存空间。
1、变量
int[] mHashes;
Object[] mArray;mHashes存储了key的hashcode,mArray存储了key和value的值。
2、put方法
public V put(K key, V value) {
final int hash;
int index;
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
h
4000
ash = key.hashCode();
index = indexOf(key, hash);
}
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
index = ~index;
if (mSize >= mHashes.length) {
final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
: (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
allocArrays(n);
if (mHashes.length > 0) {
if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
System.arraycopy(oarray, 0, mArray, 0, oarray.length);
}
freeArrays(ohashes, oarray, mSize);
}
if (index < mSize) {
if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
+ " to " + (index+1));
System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
}
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
mSize++;
return null;
}需要保证mHashes是有序数组,才可以进行二分查找。
如果新增加的数据的key对应的hashcode值在中间的位置,需要将这个索引之后的全部数据向后移动。
如果元素的个数大于数组的长度,需要进行扩容。
3、get方法
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}indexOfKey方法,是通过key对应的hashcode获取到其在mHashes数组中的索引值。内部最终调用了indexOf方法。
int indexOf(Object key, int hash) {
final int N = mSize;
// Important fast case: if nothing is in here, nothing to look for.
if (N == 0) {
return ~0;
}
int index = ContainerHelpers.binarySearch(mHashes, N, hash);
// If the hash code wasn't found, then we have no entry for this key.
if (index < 0) {
return index;
}
// If the key at the returned index matches, that's what we want.
if (key.equals(mArray[index<<1])) {
return index;
}
// Search for a matching key after the index.
int end;
for (end = index + 1; end < N && mHashes[end] == hash; end++) {
if (key.equals(mArray[end << 1])) return end;
}
// Search for a matching key before the index.
for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
if (key.equals(mArray[i << 1])) return i;
}
// Key not found -- return negative value indicating where a
// new entry for this key should go. We use the end of the
// hash chain to reduce the number of array entries that will
// need to be copied when inserting.
return ~end;
}
(1)通过二分查找,找到key在mHashes数组中的索引。
(2)通过索引找到对应的value值。
4、remove方法
public V remove(Object key) {
final int index = indexOfKey(key);
if (index >= 0) {
return removeAt(index);
}
return null;
}
(2)判断如果元素小于等于一个,将mHashes和mArray数组置空
(3)如果元素大于1个,将该索引之后的所有元素向前移一位。
如果ArrayMap容量过大(大于BASE_SIZE*2,持有数据不足),则降低ArrayMap的容量,较少内存占用。
总结:当数据量不大,插入删除不频繁,并且使用ArrayMap代替HashMap存储数据。
一、SparseArray
SparseArray的特点避免了key为int值时的自动装箱,优化了性能。
1、构造函数
public SparseArray() {
this(10);
}预先设置的容器大小为10。
2、变量
private int[] mKeys;
private Object[] mValues;两个数组进行存储,一个存储key,一个存储value。
3、put方法
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(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++;
}
}put方法中采用了二分查找比较元素key值的大小,并进行排序。
#ContainerHelpers 二分查找
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}4、get方法
public E get(int key) {
return get(key, null);
}
public E get(int key, E valueIfKeyNotFound) { int i = ContainerHelpers.binarySearch(mKeys, mSize, key); if (i < 0 || mValues[i] == DELETED) { return valueIfKeyNotFound; } else { return (E) mValues[i]; } }查找元素的方法内部也进行了二分查找。由于存储的时候都是按顺序进行排序的,所以取出元素的时候效果比较高。
5、delete方法
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}删除元素同样使用了二分查找。
总结:增加元素、删除元素、查找元素都使用了二分查找。所以SparseArray适用于数据量不大,并且key为int值的时候。
同样的,SparseBooleanArray适用于key为boolean的情况,内部实现相似。
二、ArrayMap
public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V>ArrayMap继承SimpleArrayMap,实现了Map接口。以key-value的形式存储数据,相比HashMap会占用更少的内存空间。
1、变量
int[] mHashes;
Object[] mArray;mHashes存储了key的hashcode,mArray存储了key和value的值。
2、put方法
public V put(K key, V value) {
final int hash;
int index;
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
h
4000
ash = key.hashCode();
index = indexOf(key, hash);
}
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
index = ~index;
if (mSize >= mHashes.length) {
final int n = mSize >= (BASE_SIZE*2) ? (mSize+(mSize>>1))
: (mSize >= BASE_SIZE ? (BASE_SIZE*2) : BASE_SIZE);
if (DEBUG) Log.d(TAG, "put: grow from " + mHashes.length + " to " + n);
final int[] ohashes = mHashes;
final Object[] oarray = mArray;
allocArrays(n);
if (mHashes.length > 0) {
if (DEBUG) Log.d(TAG, "put: copy 0-" + mSize + " to 0");
System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
System.arraycopy(oarray, 0, mArray, 0, oarray.length);
}
freeArrays(ohashes, oarray, mSize);
}
if (index < mSize) {
if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (mSize-index)
+ " to " + (index+1));
System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
}
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
mSize++;
return null;
}需要保证mHashes是有序数组,才可以进行二分查找。
如果新增加的数据的key对应的hashcode值在中间的位置,需要将这个索引之后的全部数据向后移动。
如果元素的个数大于数组的长度,需要进行扩容。
3、get方法
public V get(Object key) {
final int index = indexOfKey(key);
return index >= 0 ? (V)mArray[(index<<1)+1] : null;
}indexOfKey方法,是通过key对应的hashcode获取到其在mHashes数组中的索引值。内部最终调用了indexOf方法。
int indexOf(Object key, int hash) {
final int N = mSize;
// Important fast case: if nothing is in here, nothing to look for.
if (N == 0) {
return ~0;
}
int index = ContainerHelpers.binarySearch(mHashes, N, hash);
// If the hash code wasn't found, then we have no entry for this key.
if (index < 0) {
return index;
}
// If the key at the returned index matches, that's what we want.
if (key.equals(mArray[index<<1])) {
return index;
}
// Search for a matching key after the index.
int end;
for (end = index + 1; end < N && mHashes[end] == hash; end++) {
if (key.equals(mArray[end << 1])) return end;
}
// Search for a matching key before the index.
for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
if (key.equals(mArray[i << 1])) return i;
}
// Key not found -- return negative value indicating where a
// new entry for this key should go. We use the end of the
// hash chain to reduce the number of array entries that will
// need to be copied when inserting.
return ~end;
}
(1)通过二分查找,找到key在mHashes数组中的索引。
(2)通过索引找到对应的value值。
4、remove方法
public V remove(Object key) {
final int index = indexOfKey(key);
if (index >= 0) {
return removeAt(index);
}
return null;
}
public V removeAt(int index) { final Object old = mArray[(index << 1) + 1]; if (mSize <= 1) { // Now empty. if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0"); freeArrays(mHashes, mArray, mSize); mHashes = ContainerHelpers.EMPTY_INTS; mArray = ContainerHelpers.EMPTY_OBJECTS; mSize = 0; } else { if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) { // Shrunk enough to reduce size of arrays. We don't allow it to // shrink smaller than (BASE_SIZE*2) to avoid flapping between // that and BASE_SIZE. final int n = mSize > (BASE_SIZE*2) ? (mSize + (mSize>>1)) : (BASE_SIZE*2); if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n); final int[] ohashes = mHashes; final Object[] oarray = mArray; allocArrays(n); mSize--; if (index > 0) { if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0"); System.arraycopy(ohashes, 0, mHashes, 0, index); System.arraycopy(oarray, 0, mArray, 0, index << 1); } if (index < mSize) { if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + mSize + " to " + index); System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index); System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1, (mSize - index) << 1); } } else { mSize--; if (index < mSize) { if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + mSize + " to " + index); System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index); System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1, (mSize - index) << 1); } mArray[mSize << 1] = null; mArray[(mSize << 1) + 1] = null; } } return (V)old; }(1)通过key找到在mHashes数组中对应的索引
(2)判断如果元素小于等于一个,将mHashes和mArray数组置空
(3)如果元素大于1个,将该索引之后的所有元素向前移一位。
如果ArrayMap容量过大(大于BASE_SIZE*2,持有数据不足),则降低ArrayMap的容量,较少内存占用。
总结:当数据量不大,插入删除不频繁,并且使用ArrayMap代替HashMap存储数据。
相关文章推荐
- HashMap,ArrayMap,SparseArray源码分析及性能对比
- Android SparseArray 源码解析
- android sparseArray源码解析
- Android探索之旅(第十二篇)HashMap,ArrayMap,SparseArray源码分析及性能对比
- android sparseArray源码解析,比较hashmap
- Android内存优化之取代HashMap(SparseArray和ArrayMap解析)
- 谈谈源码中的SparseArray
- ArrayBlockingQueue源码解析
- STL源码解析Array
- 【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析
- 内存优化之ArrayMap源码解析
- JDK源码解析基础篇-8种基本数据类型的包装类及相关讨论
- netty5.0源码解析 ByteBuf和相关辅助类
- Fragment相关源码解析二——生命周期
- View事件分发与相关源码解析
- Java集合, ArrayBlockingQueue源码解析(常用于并发编程)
- Android SparseArray 源码详解
- 源码解析关于java阻塞容器:ArrayBlockingQueue,LinkedBlockingQueue等
- Openstack Cinder中建立volume过程的源码解析(4)----以及taskflow相关解析
- faster rcnn算法及源码及论文解析相关博客