返回
ArrayMap 揭秘:替代 HashMap 的强悍利器
Android
2023-11-23 02:33:59
前言
在 Android 开发中,HashMap 是一种常用的数据结构,用于存储键值对。然而,当数据量较小时(例如几百个),HashMap 可能不是最优选择。为了解决这个问题,Android SDK 提供了一个更适合小数据量的替代方案——ArrayMap。
ArrayMap 概述
ArrayMap 是一个基于数组实现的映射表,它继承自 HashMap,但采用了不同的存储结构和算法。与 HashMap 相比,ArrayMap 具有以下特点:
- 更快的查找速度: ArrayMap 使用数组来存储键值对,因此查找键值对的时间复杂度为 O(1),而 HashMap 使用哈希表,查找时间复杂度为 O(log n)。当数据量较小(几百个或更少)时,ArrayMap 的查找速度优势非常明显。
- 更小的内存占用: ArrayMap 使用数组存储键值对,因此内存占用更小。HashMap 使用哈希表存储键值对,哈希表需要额外的空间来存储哈希值,因此内存占用更大。
- 更简单的实现: ArrayMap 的实现比 HashMap 更简单,因为它使用数组而不是哈希表。这意味着更少的代码量和更低的维护成本。
ArrayMap 原理
ArrayMap 的基本原理是使用数组存储键值对。它将键值对存储在一个二维数组中,其中第一列存储键,第二列存储值。当需要查找一个键值对时,ArrayMap 会先在第一列中搜索键,然后在找到键后返回相应的键值对。
为了提高查找效率,ArrayMap 使用二分查找算法。二分查找算法是一种快速查找算法,它将数组分成两半,然后根据键的值来判断键值对位于哪一半。这样,就可以快速缩小查找范围,直到找到键值对。
ArrayMap 源码分析
ArrayMap 的实现位于 android.util.ArrayMap 类中。该类提供了许多方法来操作 ArrayMap,包括 put、get、remove 等。下面我们通过源码分析来了解 ArrayMap 的内部实现机制。
public class ArrayMap<K, V> extends Map<K, V> {
private static final boolean DEBUG = false;
private static final String TAG = "ArrayMap";
// 默认容量
private static final int BASE_SIZE = 4;
// 负载因子
private static final float LOAD_FACTOR = 0.8f;
// 数组
private Object[] mArray;
// 键的索引
private int[] mHashes;
// 当前数组大小
private int mSize;
// 键值对数量
private int mCapacity;
// 修改次数
private int mModCount;
// 初始化
public ArrayMap() {
this(BASE_SIZE);
}
// 根据容量初始化
public ArrayMap(int capacity) {
if (capacity == 0) {
throw new IllegalArgumentException("Capacity must be positive.");
}
mCapacity = capacity;
mHashes = new int[capacity];
mArray = new Object[capacity * 2];
mSize = 0;
}
// 获取值
@Override
public V get(Object key) {
int index = indexOfKey(key);
if (index < 0) {
return null;
}
return (V) mArray[index * 2 + 1];
}
// 设置值
@Override
public V put(K key, V value) {
int index = indexOfKey(key);
if (index < 0) {
index = insertKey(key, value);
} else {
V oldValue = (V) mArray[index * 2 + 1];
mArray[index * 2 + 1] = value;
return oldValue;
}
return null;
}
// 删除值
@Override
public V remove(Object key) {
int index = indexOfKey(key);
if (index < 0) {
return null;
}
V oldValue = (V) mArray[index * 2 + 1];
removeAt(index);
return oldValue;
}
// 扩容
private void allocArrays(int size) {
Object[] oldArray = mArray;
int[] oldHashes = mHashes;
int oldCapacity = mCapacity;
int newCapacity = (size > (3 * mCapacity) / 4) ? size : (mCapacity << 1);
if (newCapacity < size) {
newCapacity = size;
}
mHashes = new int[newCapacity];
mArray = new Object[newCapacity * 2];
mCapacity = newCapacity;
if (mSize > 0) {
for (int i = 0; i < mSize; i++) {
int index = i * 2;
Object key = oldArray[index];
Object value = oldArray[index + 1];
int hash = key == null ? 0 : key.hashCode();
int newIndex = indexOf(hash, key, newCapacity);
mHashes[newIndex] = hash;
mArray[newIndex * 2] = key;
mArray[newIndex * 2 + 1] = value;
}
}
}
// 插入键值对
private int insertKey(K key, V value) {
int hash = key == null ? 0 : key.hashCode();
int index = indexOf(hash, key, mCapacity);
mHashes[index] = hash;
mArray[index * 2] = key;
mArray[index * 2 + 1] = value;
mSize++;
if (mSize > (LOAD_FACTOR * mCapacity)) {
allocArrays(mSize * 2);
}
return index;
}
// 删除键值对
private void removeAt(int index) {
if (mSize <= 1) {
clear();
} else {
mSize--;
if (mHashes[index] == mHashes[mSize]) {
mArray[index * 2] = mArray[mSize * 2];
mArray[index * 2 + 1] = mArray[mSize * 2 + 1];
} else {
int keyIndex = indexOfKey(mArray[index * 2], mSize);
if (keyIndex == index) {
return;
}
mArray[index * 2] = mArray[keyIndex * 2];
mArray[index * 2 + 1] = mArray[keyIndex * 2 + 1];
mArray[keyIndex * 2] = null;
mArray[keyIndex * 2 + 1] = null;
}
mArray[mSize * 2] = null;
mArray[mSize * 2 + 1] = null;
}
mModCount++;
}
// 获取键的索引
private int indexOfKey(Object key) {
int hash = key == null ? 0 : key.hashCode();
for (int i = 0; i < mSize; i++) {
if (mHashes[i] == hash) {
Object arrayKey = mArray[i * 2];
if (key == arrayKey || (key != null && key.equals(arrayKey))) {
return i;
}
}
}
return -1;
}
// 获取哈希值