SparseArray与HashMap

我能想到几个原因来解释为什么带有整数键的HashMapSparseArray好得多:

  1. SparseArray的Android文档说“它通常比传统的HashMap慢”。
  2. 如果您使用HashMap而不是SparseArray来编写代码,您的代码将可以与Map的其他实现一起工作,并且您将能够使用为Map设计的所有Java API.
  3. 如果您使用HashMap而不是SparseArray编写代码,您的代码可以在非Android项目中运行。
  4. 映射覆盖equals()hashCode(),而SparseArray不覆盖。

然而,每当我尝试在Android项目中使用带有整数键的HashMap时,IntelliJ都会告诉我应该使用SparseArray。我觉得这真的很难理解。有谁知道使用SparseArray的任何令人信服的理由吗?

92954 次浏览
SparseArray

的Android文档说它通常是 比传统的HashMap慢。

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

如果使用HashMap而不是SparseArray编写代码, 将与map的其他实现一起工作,并且您将能够 使用为地图

设计的所有Java API

我认为大多数情况下,我们只使用HashMap来搜索与键相关联的值,而SparseArray在这方面做得很好。

如果使用HashMap而不是SparseArray编写代码, 将在非Android项目中工作。

SparseArray的源代码相当简单且易于理解,因此您只需花费很少的精力将其移动到其他平台(通过简单的复制和粘贴)。

映射覆盖了equals()和hashCode(),而SparseArray则没有。

我只能说,(对大多数开发人员来说)谁在乎呢?

SparseArray的另一个重要方面是它仅使用数组来存储所有元素,而HashMap使用Entry,因此SparseArrayHashMap消耗少得多的存储器,见

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

这只是来自其稀疏阵列的文档的警告:

与使用HashMap相比,

它的内存效率更高。 将整数映射到对象

与使用常规HashMap相比,使SparseArray内存高效,即不允许数组内有多个间隙,不像HashMap.没有什么可担心的,如果您不想担心设备的内存分配,您可以使用传统的HashMap.

Java中的稀疏数组是一种将键映射到值的数据结构。与地图相同的想法,但不同的实现:

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

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

SparseArray的主要优点是它通过使用原语而不是对象作为键来节省内存。

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

Sparsearrays有自己的位置。考虑到他们使用二进制搜索算法来查找数组中的值,您必须考虑您在做什么。二分查找是O(log n),而哈希查找是O(1)。这并不一定意味着对于一组给定的数据,二分搜索更慢。但是,随着条目数量的增加,哈希表的功能将发挥作用。因此,条目数量较少的注释可能与使用HashMap相同,并且可能比使用HashMap更好。

HashMap只能和哈希一样好,也会受到加载因子的影响(我认为在以后的版本中,他们会忽略加载因子,以便更好地优化它)。他们还添加了一个二级哈希,以确保哈希是好的。这也是为什么SparseArray对于相对较少的条目(<100)非常有效的原因。

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

有了我们简化的Multi-Dex建筑,你甚至不需要重新包装你所需要的东西。(Trove有很多课程)

经过一些谷歌搜索,我尝试添加一些信息到已经发布的Anwers:

艾萨克·泰勒对SparseArrays和HashMaps进行了性能比较。他说

HashMap和SparseArray的数据结构非常相似 尺寸在1,000

以下

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

失去性能

EdgBlog的比较表明,SparseArray需要的内存比HashMap少得多,因为它的键更小(int对integer),而且

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

作为一个结论,我想说的是,如果你要在你的地图中存储大量的数据,这种差异可能很重要。否则,忽略警告即可。

当键是基元类型时,

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;
}

类=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;
}

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

来源:幻灯片90中的罗曼·盖伊的《安卓记忆》

上面

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

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

额外信息可从官方Oracle文档获得。

类=12字节+(n个实例变量)*4字节
数组=20字节+(n个元素)*(元素大小)
条目=32字节+(第1个元素大小)+(第2个元素大小)

我来这里只是想要一个如何使用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参数是整数键。

查找整型键的值

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

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

如果要避免为缺少的键获取null,则可以使用get(int key, E valueIfKeyNotFound)

迭代这些项

您可以使用keyAtvalueAtSomeIndex来遍历集合,因为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

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