Java数据结构够多了吧,为了不同角度的性能又提出了SparseArray等系列。直译就是稀疏数组。实际好多同Map还有HashMap作比较实际有啥好比较的。HashMap的key可以除开基本类型的任意类型,但是SparseArray实际的key值是int,跟ArrayList这种数组才有可比性。ArrayList就是简单利用数组来做的。
SparseArray 也是利用数组,不过key是一个数组,value是一个数组。为啥要这么做,为啥体现稀疏性,来看看put和get方法便知。
public void put(int key, E value) {
//在mkey数组查找是否有此key
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
//命中。
if (i >= 0) {
//直接修改了。
mValues[i] = value;
} else {
//binarySearch 会返回来一个~i,这个时候折回去。
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++;
}
}
现在看看get方法。
public E get(int key, E valueIfKeyNotFound) {
//也是先二分查找相关key。如果找到就直接取值,找不到就null。
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
return valueIfKeyNotFound;
} else {
return (E) mValues[i];
}
}
可以看到学习这个关键在于这个key的数组是个升序排列,然后通过二分查找。这样确实比较快,对于android性能相较于HashMap有提升这就是SparseArray存在的意义,注意安卓对性能提升非常关注。
再看看ArrayMap 对性能提升有什么影响。看下构造方法。
public ArrayMap(int capacity, boolean identityHashCode) {
mIdentityHashCode = identityHashCode;
// If this is immutable, use the sentinal EMPTY_IMMUTABLE_INTS
// instance instead of the usual EmptyArray.INT. The reference
// is checked later to see if the array is allowed to grow.
if (capacity < 0) {
mHashes = EMPTY_IMMUTABLE_INTS;
mArray = EmptyArray.OBJECT;
} else if (capacity == 0) {
mHashes = EmptyArray.INT;
mArray = EmptyArray.OBJECT;
} else {
allocArrays(capacity);
}
mSize = 0;
}
mHashed就是key的hash值,mArray就是value值。
为啥要多出来一个mHashes呢,可以看下这个put和get方法。
public V put(K key, V value) {
final int osize = mSize;
final int hash;
int index;
if (key == null) {
hash = 0;
index = indexOfNull();
} else {
hash = mIdentityHashCode ? System.identityHashCode(key) : key.hashCode();
//查询key对于的index。如果不存在会是一个经过二分查找不存在的index,取反。
index = indexOf(key, hash);
}
//存在,那么需要存储的位置2*index+1.
if (index >= 0) {
index = (index<<1) + 1;
final V old = (V)mArray[index];
mArray[index] = value;
return old;
}
//不存在。
index = ~index;
if (osize >= mHashes.length) {
final int n = osize >= (BASE_SIZE*2) ? (osize+(osize>>1))
: (osize >= 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 (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
throw new ConcurrentModificationException();
}
if (mHashes.length > 0) {
if (DEBUG) Log.d(TAG, "put: copy 0-" + osize + " to 0");
System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
System.arraycopy(oarray, 0, mArray, 0, oarray.length);
}
freeArrays(ohashes, oarray, osize);
}
if (index < osize) {
if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
+ " to " + (index+1));
System.arraycopy(mHashes, index, mHashes, index + 1, osize - index);
System.arraycopy(mArray, index << 1, mArray, (index + 1) << 1, (mSize - index) << 1);
}
if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
if (osize != mSize || index >= mHashes.length) {
throw new ConcurrentModificationException();
}
}
//存值。
mHashes[index] = hash;
mArray[index<<1] = key;
mArray[(index<<1)+1] = value;
mSize++;
return null;
}
可以看到mArray的排列,是key和value间隔排列的。然后还有个hash数组。这样首先通过hash数组确立具体的index值。然后通过index值去找到相应的value。这个其实有点类似SparseArray的加强版,因为它能够key值为object。现在总结下类似key,vaue结构的数据结构。一般有两种方法,一种是Hash式存储,这种一般是解决key值为Object的情况。