在 Android 开发中,选择合适的键值对数据结构对优化性能和内存使用至关重要。两种常用的选项是 SparseArray
和 HashMap
。本文将探讨它们的区别、优缺点以及使用场景。
什么是 SparseArray?
SparseArray
是 Android SDK 中的类,用于将整数键(int)与对象映射,针对移动设备等资源受限环境优化了内存效率。它避免了保存键值对映射时键为 int 类型时将 Integer
对象装箱的开销,内部使用两个数组(一个存储 int 类型的键,一个存储值)来存储数据。
SparseArray maps integers to Objects and, unlike a normal array of Objects, its indices can contain gaps. SparseArray is intended to be more memory-efficient than a HashMap, because it avoids auto-boxing keys and its data structure doesn't rely on an extra entry object for each mapping.
这段是 SparseArray 的注释,解释了该类的作用:使用 int[]
数组存放 key
,避免了 HashMap
中基本数据类型需要装箱的步骤,其次不使用额外的结构体(Entry),单个元素的存储成本下降。
优缺点对比
SparseArray 的优点
内存效率高:使用原生
int
类型作为键,避免使用Integer
装箱,减少内存使用。为 Android 优化:专为 Android 资源受限环境设计,适合小型数据集。
专用变种:Android 还提供了
LongSparseArray
(键为 long 类型)、SparseBooleanArray
(值为 boolean 类型)等变种,针对特定场景进一步优化。
SparseArray 的缺点
键的类型受限制:仅支持 int 或 long 类型的键,灵活性较低。
大数据量性能较差:获取数据时使用二分法查找,时间复杂度为 O(log n),对于大数据量查询效率低于
HashMap
。功能有限:缺乏高级功能,如 putAll 或复杂迭代。
HashMap 的优点
键类型灵活:支持任意对象类型作为键(如 String、自定义对象)。
高效查询:平均 O(1) 的时间复杂度,适合大数据的量场景。
功能丰富:提供丰富的 API,支持复杂数据操作。
HashMap 缺点
内存开销大:键值装箱,哈希表,和集合满后每次扩容都是2的N次方成倍扩容,这些开销导致内存占用较高。
垃圾回收压力:装箱操作生成更多对象,可能增加 Android 上的垃圾回收频率。
简单场景复杂化:对于简单的整数键场景,
HashMap
的实现过于复杂。
使用场景
使用 SparseArray:当键为整数、且数据集较小(数百项以内),内存效率在应用中也至关重要时。
例如:
a. 将 View ID 做为 key 映射到 View,根据 View ID 可用更高效的查找到 View。
b.
Bundle
和Intent
支持将SparseArray
放入其中进行跨组件的数据传递。c. 根据某个整数状态码或配置项 ID 存储对应的配置对象。
使用 HashMap:当键为非整数类型、数据集较大或需要高级功能时。
通过 Bundle
中传递的 SparseArray
的示例
假设你从一个 Activity
传递了一个包含 SparseArray
的 Bundle
到另一个 Activity
或 Fragment
。
发送端 Activity:
package cn.itmob.sample.sparsearray
import android.content.Intent
import android.os.Bundle
import android.util.SparseArray
import androidx.appcompat.app.AppCompatActivity
import android.widget.Button
class SampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 假设页面中有一个按钮
val button = Button(this).apply {
text = "发送数据"
setOnClickListener {
val sparseArray = SparseArray<String>()
sparseArray.put(1, "itmob")
sparseArray.put(10, "itmob.cn")
sparseArray.put(20, "<https://itmob.cn>")
val bundle = Bundle()
bundle.putSparseParcelableArray("data", sparseArray)
val intent = Intent(this@SampleActivity, ReceiverActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
}
setContentView(button)
}
}
接收端 Activity:
package cn.itmob.sample.parsearray
import android.os.Bundle
import android.util.SparseArray
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class ReceiverActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val textView = TextView(this)
setContentView(textView)
val bundle = intent.extras
if (bundle != null) {
// 从 Bundle 中获取 SparseArray
val data = bundle.getSparseParcelableArray("data") as? SparseArray<String>
data?.let {
val stringBuilder = StringBuilder("接收到的第一个数据:\\n")
val key = it.keyAt(0)
val value = it.get(key)
textView.text = "key: $key, value: $value"
} ?: run {
textView.text = "未接收到颜色数据或数据类型不匹配。"
}
} else {
textView.text = "未接收到 Bundle 数据。"
}
}
}