数据结构 - ArrayMap 和 SparseArray的区别

173 阅读3分钟

一、基本结构对比

// ArrayMap的基本结构
class ArrayMap<K, V> {
    // 用于存储hash值的数组
    private var mHashes: IntArray
    // 用于存储key和value的数组,长度是mHashes的2倍
    private var mArray: Array<Any?>
}

// SparseArray的基本结构
class SparseArray<V> {
    // 用于存储key的数组(必须是int类型)
    private var mKeys: IntArray
    // 用于存储value的数组
    private var mValues: Array<Any?>
}

二、主要区别

  1. 键的类型限制
// ArrayMap可以使用任意类型作为key
val arrayMap = ArrayMap<String, String>()  // key可以是任意类型
arrayMap["key"] = "value"

// SparseArray只能使用int作为key
val sparseArray = SparseArray<String>()    // key只能是int
sparseArray.put(1, "value")
  1. 内存占用
class MemoryExample {
    fun memoryComparison() {
        // ArrayMap需要额外的内存来存储key的hash值
        val arrayMap = ArrayMap<Int, String>()  // 需要三个数组空间
        
        // SparseArray直接使用int作为key,不需要额外的hash计算
        val sparseArray = SparseArray<String>() // 只需要两个数组空间
    }
}
  1. 查找方式
class SearchExample {
    fun searchComparison() {
        // ArrayMap使用二分查找hash值
        val arrayMap = ArrayMap<String, String>()
        arrayMap["key"] = "value"
        // 查找时:1.计算key的hash值 2.二分查找hash数组 3.获取值
        
        // SparseArray直接对key进行二分查找
        val sparseArray = SparseArray<String>()
        sparseArray.put(1, "value")
        // 查找时:直接二分查找key数组
    }
}

三、性能对比

class PerformanceTest {
    fun comparePerformance() {
        val count = 1000
        
        // 1. ArrayMap测试
        val arrayMap = ArrayMap<Int, String>()
        val arrayMapTime = measureTimeMillis {
            for (i in 0 until count) {
                arrayMap[i] = "value$i"
            }
            for (i in 0 until count) {
                arrayMap[i]
            }
        }
        
        // 2. SparseArray测试
        val sparseArray = SparseArray<String>()
        val sparseArrayTime = measureTimeMillis {
            for (i in 0 until count) {
                sparseArray.put(i, "value$i")
            }
            for (i in 0 until count) {
                sparseArray.get(i)
            }
        }
        
        println("ArrayMap time: $arrayMapTime")
        println("SparseArray time: $sparseArrayTime")
    }
}

四、使用场景

  1. ArrayMap适用场景
// 1. 需要使用非int类型作为key时
val userMap = ArrayMap<String, User>()
userMap["user1"] = User()

// 2. 数据量较小(几百个元素以内)时
val smallDataMap = ArrayMap<Any, Any>()

// 3. 内存敏感但又不能使用SparseArray时
class MemorySensitiveClass {
    private val map = ArrayMap<String, Int>(10) // 指定初始容量
}
  1. SparseArray适用场景
// 1. key为int类型时
val viewCache = SparseArray<View>()
viewCache.put(R.id.button, button)

// 2. 对性能要求较高时
class HighPerformanceCache {
    private val cache = SparseArray<Data>()
    
    fun getData(id: Int): Data? {
        return cache.get(id)
    }
}

// 3. 需要频繁增删改查时
class FrequentOperations {
    private val dataStore = SparseArray<String>()
    
    fun updateData(id: Int, value: String) {
        dataStore.put(id, value)
    }
}

五、内部实现差异

class InternalDifference {
    fun explainDifference() {
        // 1. ArrayMap的put操作
        val arrayMap = ArrayMap<String, Int>()
        arrayMap["key"] = 1
        // 内部流程:
        // 1.计算key的hashCode
        // 2.二分查找hash数组定位位置
        // 3.将hash值存入hash数组
        // 4.将key和value存入对象数组
        
        // 2. SparseArray的put操作
        val sparseArray = SparseArray<Int>()
        sparseArray.put(1, 1)
        // 内部流程:
        // 1.直接二分查找key数组定位位置
        // 2.将key和value分别存入两个数组
    }
}

六、选择建议

class UsageGuide {
    fun chooseContainer() {
        // 1. 数据量小,key类型多样
        val smallMap = ArrayMap<Any, Any>()
        
        // 2. key为int,注重性能
        val idCache = SparseArray<Data>()
        
        // 3. 数据量大,不考虑内存
        val largeMap = HashMap<String, String>()
        
        // 4. key为基本类型的特殊情况
        val intMap = SparseArray<String>()       // key为int
        val longMap = SparseLongArray()          // key为long
        val booleanMap = SparseBooleanArray()    // key为boolean
    }
}

主要区别总结:

  1. ArrayMap可以使用任意类型作为key,而SparseArray只能用int
  2. SparseArray内存占用更少,因为不需要存储hash值
  3. SparseArray在key为int时性能更好,因为避免了hash计算
  4. ArrayMap更通用,但内存占用稍大
  5. 两者都适合数据量较小的场景(几百个元素以内)

选择建议:

  • 如果key是int类型,优先使用SparseArray
  • 如果key是其他类型,且数据量小,使用ArrayMap
  • 如果数据量大,考虑使用HashMap