ITmob-Ly
发布于 2024-11-25 / 2 阅读
0

在 Android 中 SparseArray 与 HashMap 的对比,又是在什么场景下来使用它们?

在 Android 开发中,选择合适的键值对数据结构对优化性能和内存使用至关重要。两种常用的选项是 SparseArrayHashMap。本文将探讨它们的区别、优缺点以及使用场景。

什么是 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. BundleIntent 支持将 SparseArray 放入其中进行跨组件的数据传递。

    c. 根据某个整数状态码或配置项 ID 存储对应的配置对象。

  • 使用 HashMap:当键为非整数类型、数据集较大或需要高级功能时。

通过 Bundle 中传递的 SparseArray 的示例

假设你从一个 Activity 传递了一个包含 SparseArrayBundle 到另一个 ActivityFragment

发送端 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 数据。"
        }
    }
}