SparseArray vs HashMap

时间:2014-08-29 01:59:08

标签: java android hashmap sparse-matrix

我可以想到为什么带有整数键的HashMapSparseArray s好得多的原因有几个:

  1. SparseArray的Android文档说"它通常比传统的HashMap"慢。
  2. 如果您使用HashMap而不是SparseArray编写代码,您的代码将与Map的其他实现一起使用,您将能够使用为地图设计的所有Java API。
  3. 如果您使用HashMap而不是SparseArray编写代码,您的代码将在非Android项目中运行。
  4. 地图会覆盖equals()hashCode(),而SparseArray则不会。
  5. 然而,每当我尝试在Android项目中使用带有整数键的HashMap时,IntelliJ告诉我应该使用SparseArray。我觉得这很难理解。有没有人知道使用SparseArray的任何令人信服的理由?

7 个答案:

答案 0 :(得分:206)

当密钥是基本类型时,

SparseArray可用于替换HashMap。 对于不同的键/值类型,存在一些变体,即使并非所有键/值都是公开可用的。

好处是:

  • 分配 - 自由
  • 没有拳击

缺点:

  • 通常较慢,不适用于大型藏品
  • 他们无法在非Android项目中使用

HashMap可以替换为以下内容:

SparseArray          <Integer, Object>
SparseBooleanArray   <Integer, Boolean>
SparseIntArray       <Integer, Integer>
SparseLongArray      <Integer, Long>
LongSparseArray      <Long, Object>
LongSparseLongArray  <Long, Long>   //this is not a public class                                 
                                    //but can be copied from  Android source code 

就记忆而言,以下是1000个元素的SparseIntArrayHashMap<Integer, Integer>的示例:

SparseIntArray

class SparseIntArray {
    int[] keys;
    int[] values;
    int size;
}

Class = 12 + 3 * 4 = 24字节
数组= 20 + 1000 * 4 = 4024字节
总计= 8,072字节

HashMap

class HashMap<K, V> {
    Entry<K, V>[] table;
    Entry<K, V> forNull;
    int size;
    int modCount;
    int threshold;
    Set<K> keys
    Set<Entry<K, V>> entries;
    Collection<V> values;
}

Class = 12 + 8 * 4 = 48字节
进入= 32 + 16 + 16 = 64字节
数组= 20 + 1000 * 64 = 64024字节
总计= 64,136字节

来自幻灯片90的Android Memories by Romain Guy

上面的数字是JVM在堆上分配的内存量(以字节为单位)。 它们可能因所使用的特定JVM而异。

java.lang.instrument包中包含一些有用的高级操作方法,例如使用getObjectSize(Object objectToSize)检查对象的大小。

官方Oracle documentation提供了额外信息。

Class = 12字节+(n个实例变量)* 4个字节
数组= 20个字节+(n个元素)*(元素大小)
Entry = 32字节+(第一元素大小)+(第二元素大小)

答案 1 :(得分:24)

我来到这里只是想要一个如何使用SparseArray的例子。这是对此的补充答案。

创建SparseArray

SparseArray<String> sparseArray = new SparseArray<>();

SparseArray将整数映射到某些Object,因此您可以将上述示例中的String替换为任何其他Object。如果要将整数映射到整数,请使用SparseIntArray

添加或更新项目

使用put(或append)向阵列添加元素。

sparseArray.put(10, "horse");
sparseArray.put(3, "cow");
sparseArray.put(1, "camel");
sparseArray.put(99, "sheep");
sparseArray.put(30, "goat");
sparseArray.put(17, "pig");

请注意,int键不需要按顺序排列。这也可用于更改特定int键的值。

删除项目

使用remove(或delete)删除数组中的元素。

sparseArray.remove(17); // "pig" removed

int参数是整数键。

查找int键的值

使用get获取某个整数键的值。

String someAnimal = sparseArray.get(99);  // "sheep"
String anotherAnimal = sparseArray.get(200); // null

如果您想避免null丢失密钥,可以使用get(int key, E valueIfKeyNotFound)

迭代项目

您可以使用keyAtvalueAt某些索引来循环收集集合,因为SparseArray维护了一个与int密钥不同的单独索引。

int size = sparseArray.size();
for (int i = 0; i < size; i++) {

    int key = sparseArray.keyAt(i);
    String value = sparseArray.valueAt(i);

    Log.i("TAG", "key: " + key + " value: " + value);
}

// key: 1 value: camel
// key: 3 value: cow
// key: 10 value: horse
// key: 30 value: goat
// key: 99 value: sheep

请注意,键按升序排序,而不是按照添加顺序排序。

答案 2 :(得分:17)

  

然而每当我尝试在android中使用带有整数键的HashMap时   项目,intelliJ告诉我,我应该使用SparseArray。

这只是来自documentation稀疏数组的警告:

  

它的目的是比使用HashMap更高效   将整数映射到对象

SparseArray比使用常规HashMap更强内存效率,这不允许数组中的多个间隙不像HashMap。没有什么可担心的,如果您不想担心设备的内存分配,可以使用传统的HashMap。

答案 3 :(得分:10)

经过一些谷歌搜索后,我尝试将一些信息添加到已经发布的答案中:

Isaac Taylor对SparseArrays和Hashmaps进行了性能比较。他说那是

  

Hashmap和SparseArray对于数据结构非常相似   尺寸低于1,000

  

当大小增加到10,000标记[...] Hashmap时   在添加对象时具有更高的性能,而SparseArray具有   检索对象时的更高性能。 [...]大小为100,000 [...]时,Hashmap会很快失去性能

Edgblog的比较表明SparseArray需要比HashMap少得多的内存,因为密钥较小(int vs Integer),事实是

  

HashMap.Entry实例必须跟踪对的引用   键,值和下一个条目。此外,它还需要存储   条目的哈希值为int。

作为结论,我会说如果要在地图中存储大量数据,差异可能很重要。否则,只需忽略警告。

答案 4 :(得分:9)

Java中的稀疏数组是将键映射到值的数据结构。与Map相同,但实现方式不同:

  1. Map在内部表示为列表数组,其中这些列表中的每个元素都是键值对。键和值都是对象实例。

  2. 稀疏数组简单地由两个数组组成:(基元)键的数组和(对象)值的数组。这些数组索引可能存在间隙,因此称为“稀疏”数组。

  3. SparseArray的主要兴趣在于它通过使用基元而不是对象作为关键来节省内存。

答案 5 :(得分:2)

  

SparseArray的android文档说“通常是   慢于传统的HashMap“。

是的,没错。但是当你只有10或20个项目时,性能差异应该是微不足道的。

  

如果您使用HashMaps而不是SparseArrays编写代码   将与Map的其他实现一起使用,您将能够   使用为Maps

设计的所有Java API

我认为我们通常只使用HashMap来搜索与某个键相关联的值,而SparseArray确实很擅长这一点。

  

如果您使用HashMaps而不是SparseArrays编写代码   将在非Android项目中工作。

SparseArray的源代码非常简单易懂,因此您只需花费很少精力将其移至其他平台(通过简单的COPY&amp; Paste)。

  

映射覆盖equals()和hashCode(),而SparseArray不是

我能说的是(对于大多数开发者)谁在乎?

SparseArray的另一个重要方面是它只使用数组来存储HashMap使用Entry时的所有元素,因此SparseArray的成本显着低于HashMap 1}},请参阅this

答案 6 :(得分:1)

不幸的是编译器发出警告。我猜HashMap已被过度用于存储项目。

SparseArrays有它们的位置。鉴于他们使用二进制搜索算法在数组中查找值,您必须考虑您正在做什么。二进制搜索是O(log n),而哈希查找是O(1)。这并不一定意味着对于给定的数据集,二进制搜索较慢。但是,随着条目数量的增加,哈希表的功能将取代。因此,条目数量少,可能比使用HashMap更好的注释。

HashMap只能与哈希一样好,也可能受到加载因子的影响(我认为在以后的版本中,它们会忽略加载因子,因此可以更好地优化)。他们还添加了一个二级哈希,以确保哈希是好的。 SparseArray也适用于相对较少的条目(&lt; 100)。

我建议如果你需要一个哈希表,并希望更好的内存使用原始整数(没有自动装箱)等,试试trove。 (http://trove.starlight-systems.com - LGPL许可证)。 (与他们的图书馆没有任何隶属关系)

通过简化的多dex建筑,我们甚至不需要根据您的需要重新包装。 (特洛伊有很多课程)