HashMap
参考:
SparseArray
-
其内部主要通过 2 个数组来存储 key 和 value,分别是 int[] 和 Object[]。这也限定了其 key 只能为 int 类型,且 key 不能重复,否则会发生覆盖。
-
一切操作都是基于二分查找算法,将 key 以升序的方法 “紧凑” 的排列在一起,从而提高内存的利用率以及访问的效率。相比较 HashMap 而言,这是典型的时间换空间的策略。
-
删除操作并不是真的删除,而只是标记为 DELETE,以便下次能够直接复用。
SparseArray
是android里为<Interger,Object>这样的Hashmap而专门写的类,目的是提高内存效率,其核心是折半二分查找函数(binarySearch)。
注意内存二字很重要,因为它仅仅提高内存效率,而不是提高执行效率,
它只适用于android系统(内存对android项目有多重要,地球人都知道)。
SparseArray有两个优点:
- 1.避免了自动装箱(auto-boxing),
- 2.数据结构不会依赖于外部对象映射。
我们知道HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置,存放的都是数组元素的引用,通过每个对象的hash值来映射对象。而SparseArray则是用数组数据结构来保存映射,然后通过折半查找来找到对象。但其实一般来说,SparseArray执行效率比HashMap要慢一点,因为查找需要折半查找,而添加删除则需要在数组中执行,而HashMap都是通过外部映射。但相对来说影响不大,最主要是SparseArray不需要开辟内存空间来额外存储外部映射,从而节省内存。
//SparseArray
public class SparseArray<E> implements Cloneable {
private int[] mKeys;
private Object[] mValues;
private int mSize;
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];
}
}
}
//SparseBooleanArray
public class SparseBooleanArray implements Cloneable {
private int[] mKeys;
private boolean[] mValues;
private int mSize;
public boolean get(int key, boolean valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
} else {
return mValues[i];
}
}
}
//SparseIntArray
public class SparseIntArray implements Cloneable {
private int[] mKeys;
private int[] mValues;
private int mSize;
public int get(int key, int valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
} else {
return mValues[i];
}
}
}
//SparseLongArray
public class SparseLongArray implements Cloneable {
private int[] mKeys;
private long[] mValues;
private int mSize;
public long get(int key, long valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
} else {
return mValues[i];
}
}
}
ArrayMap
参考: ArrayMap详解及源码分析
-
mHashs 数组以升序的方式保存了所有的 hash code,在查找数据时则通过二分查找 hash code 所对应的 index。这也是它的 get() 比 HashMap 慢的根据原因所在。
-
通过 hash code 在 mHashs 数组里的 index 值来确定 key 以及 value 在 mArrays 数组中的存储位置。一般来说分别就是 index << 1 以及 index << 1 + 1。再简单点说就是 index * 2 以及 index * 2 + 1。
-
hashCode 必然可能存在冲突,这里是怎么解决的呢?简单点说就是,在 mHashs 中相邻地存多份 hash code,而在 mArray 中分别以它们的 index 来计算 key-value 的存储位置。
-
当进行 remove 操作时,在一定条件下,可能会发生数据的压缩,从而节省内存的使用。
public final class ArrayMap<K, V> implements Map<K, V> {
int[] mHashes;
Object[] mArray;
int mSize;
private static int binarySearchHashes(int[] hashes, int N, int hash) {
try {
return ContainerHelpers.binarySearch(hashes, N, hash);
} catch (ArrayIndexOutOfBoundsException e) {
if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
throw new ConcurrentModificationException();
} else {
throw e; // the cache is poisoned at this point, there's not much we can do
}
}
}
}
ContainerHelpers
package android.util;
class ContainerHelpers {
// 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) {
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
}
static int binarySearch(long[] array, int size, long value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final long 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
}
}
什么时候使用SparseArray/ArrayMap
1、如果key的类型已经确定为int类型,那么使用SparseArray,因为它避免了自动装箱的过程,
如果key为long类型,它还提供了一个LongSparseArray来确保key为long类型时的使用
2、如果key类型为其它的类型,则使用ArrayMap
demo
//1、适用于键为int的map数据结构类型
//key为int的时候才能使用,注意是int而不是Integer,
// 这也是sparseArray效率提升的一个点,去掉了装箱的操作
//2、适用于小规模数据存储
//3、内部使用二分查找进行数据查询,查询效率高
//4、不需要单独开辟内存来映射对象,节约内存
SparseArray<String> intSparseArray = new SparseArray<>();
intSparseArray.put(1, "java");
intSparseArray.put(2, "js");
intSparseArray.put(3, "kotlin");
intSparseArray.put(4, "flutter");
ArrayMap<String, String> arrayMap = new ArrayMap<>();
arrayMap.put("1", "java");
arrayMap.put("2", "js");
arrayMap.put("3", "kotlin");
arrayMap.put("4", "flutter");
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("1", "java");
hashMap.put("2", "js");
hashMap.put("3", "kotlin");
hashMap.put("4", "flutter");