Je peux penser à plusieurs raisons pour lesquelles HashMap
s avec des clés entières sont bien meilleurs que SparseArray
s:
SparseArray
indique: "Elle est généralement plus lente qu'un système traditionnel HashMap
".HashMap
s plutôt que SparseArray
s, votre code fonctionnera avec d'autres implémentations de Map et vous pourrez utiliser toutes les API Java conçues pour Maps.HashMap
s plutôt que SparseArray
s, votre code fonctionnera dans des projets non-Android.equals()
et hashCode()
alors que SparseArray
ne le fait pas.Pourtant, chaque fois que j'essaie d'utiliser un HashMap
avec des clés entières dans un projet Android, IntelliJ me dit que je devrais utiliser plutôt un SparseArray
. Je trouve cela vraiment difficile à comprendre. Est-ce que quelqu'un connaît des raisons impérieuses d'utiliser SparseArray
s?
SparseArray
peut être utilisé pour remplacer HashMap
lorsque la clé est un type primitif. Il existe certaines variantes pour différents types de clé/valeur, même si elles ne sont pas toutes accessibles au public.
Les avantages sont:
Désavantages:
HashMap
peut être remplacé par le suivant:
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
En termes de mémoire, voici un exemple de SparseIntArray
vs HashMap<Integer, Integer>
pour 1000 éléments:
SparseIntArray
:
class SparseIntArray {
int[] keys;
int[] values;
int size;
}
Classe = 12 + 3 * 4 = 24 octets
Tableau = 20 + 1000 * 4 = 4024 octets
Total = 8 072 octets
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;
}
Classe = 12 + 8 * 4 = 48 octets
Entrée = 32 + 16 + 16 = 64 octets
Tableau = 20 + 1000 * 64 = 64024 octets
Total = 64 136 octets
Source: Souvenirs Android de Romain Guy extrait de la diapositive 90.
Les nombres ci-dessus correspondent à la quantité de mémoire (en octets) allouée sur le tas par la machine virtuelle Java. Ils peuvent varier en fonction de la JVM spécifique utilisée.
Le package Java.lang.instrument
contient des méthodes utiles pour des opérations avancées, telles que la vérification de la taille d'un objet avec getObjectSize(Object objectToSize)
.
Des informations supplémentaires sont disponibles sur le site officiel documentation Oracle .
Classe = 12 octets + (n variables d'instance) * 4 octets
Tableau = 20 octets + (n éléments) * (taille de l'élément)
Entrée = 32 octets + (taille du premier élément) + (taille du deuxième élément)
Je suis venu ici juste pour vouloir un exemple d'utilisation SparseArray
. Ceci est une réponse supplémentaire pour cela.
SparseArray<String> sparseArray = new SparseArray<>();
Un SparseArray
mappe des entiers avec un certain Object
, de sorte que vous puissiez remplacer String
dans l'exemple ci-dessus par un autre Object
. Si vous mappez des entiers à des entiers, utilisez SparseIntArray
.
Utilisez put
(ou append
) pour ajouter des éléments au tableau.
sparseArray.put(10, "horse");
sparseArray.put(3, "cow");
sparseArray.put(1, "camel");
sparseArray.put(99, "sheep");
sparseArray.put(30, "goat");
sparseArray.put(17, "pig");
Notez que les touches int
n'ont pas besoin d'être en ordre. Ceci peut également être utilisé pour changer la valeur d'une clé int
particulière.
Utilisez remove
(ou delete
) pour supprimer des éléments du tableau.
sparseArray.remove(17); // "pig" removed
Le paramètre int
est la clé entière.
Utilisez get
pour obtenir la valeur d'une clé entière.
String someAnimal = sparseArray.get(99); // "sheep"
String anotherAnimal = sparseArray.get(200); // null
Vous pouvez utiliser get(int key, E valueIfKeyNotFound)
si vous souhaitez éviter d'obtenir null
pour les clés manquantes.
Vous pouvez utiliser keyAt
et valueAt
un index pour parcourir la collection, car SparseArray
conserve un index distinct, distinct des clés 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
Notez que les clés sont classées par ordre croissant, pas dans l'ordre dans lequel elles ont été ajoutées.
Pourtant, chaque fois que j'essaie d'utiliser un HashMap avec des clés entières dans un projet Android, IntelliJ me dit que je devrais plutôt utiliser un SparseArray.
Ce n'est qu'un avertissement de cette documentation de son tableau fragmenté:
Il est conçu pour être plus efficace en termes de mémoire que l'utilisation d'un objet HashMap pour mapper des entiers sur des objets.
La SparseArray
est conçue pour être efficace en mémoire par rapport à l'utilisation de HashMap standard, c'est-à-dire qu'elle n'autorise pas plusieurs espaces vides dans le tableau, contrairement à HashMap. Il n'y a rien à craindre, vous pouvez utiliser le HashMap traditionnel si vous ne souhaitez pas vous soucier de l'allocation de mémoire au périphérique.
Après quelques recherches sur Google, j'essaie d'ajouter des informations aux réponses déjà publiées:
Isaac Taylor a comparé les performances de SparseArrays et de Hashmaps. Il affirme que
hashmap et SparseArray sont très similaires pour les tailles de structure de données inférieures à 1 000.
et
lorsque la taille a été augmentée à 10 000 [...] marques, le tableau de hachage offre de meilleures performances lors de l'ajout d'objets, tandis que SparseArray offre de meilleures performances lors de la récupération d'objets. [...] À une taille de 100 000 [...], Hashmap perd très rapidement ses performances
Une comparaison sur Edgblog montre qu'un SparseArray nécessite beaucoup moins de mémoire qu'un HashMap en raison de la plus petite clé (int vs Integer) et du fait que
une instance HashMap.Entry doit garder une trace des références pour la clé, la valeur et l'entrée suivante. De plus, il doit également stocker le hachage de l'entrée sous forme d'int.
En conclusion, je dirais que la différence pourrait avoir une importance si vous allez stocker beaucoup de données dans votre carte. Sinon, ignorez simplement l'avertissement.
Un tableau fragmenté dans Java est une structure de données qui mappe les clés aux valeurs. Même idée qu'une carte, mais implémentation différente:
Une carte est représentée en interne sous la forme d'un tableau de listes, où chaque élément de ces listes est une paire clé-valeur. La clé et la valeur sont des instances d'objet.
Un tableau fragmenté est simplement constitué de deux tableaux: un tableau de clés (primitives) et un tableau de valeurs (objets). Il peut y avoir des lacunes dans les indices de ces tableaux, d'où le terme "tableau épars".
L'intérêt principal de SparseArray est qu'il économise de la mémoire en utilisant des primitives au lieu d'objets comme clé.
La documentation de Android d'un SparseArray indique "En règle générale, il est plus lent qu'un HashMap traditionnel".
Oui c'est vrai. Mais lorsque vous ne disposez que de 10 ou 20 éléments, la différence de performances doit être insignifiante.
Si vous écrivez du code en utilisant HashMaps plutôt que SparseArrays, votre code fonctionnera avec d'autres implémentations de Map et vous pourrez utiliser toutes les API Java conçues pour Maps.
Je pense que le plus souvent, nous utilisons uniquement HashMap
pour rechercher une valeur associée à une clé alors que SparseArray
est vraiment bon à cela.
Si vous écrivez du code en utilisant HashMaps plutôt que SparseArrays, votre code fonctionnera dans des projets non-Android.
Le code source de SparseArray est assez simple et facile à comprendre, de sorte que vous ne payez que peu d'effort pour le déplacer vers d'autres plates-formes (via un simple COPY & Paste).
La carte remplace equals () et hashCode () alors que SparseArray ne le fait pas
Tout ce que je peux dire, c'est (à la plupart des développeurs) qui s'en soucie?
Un autre aspect important de SparseArray
est qu’il utilise uniquement un tableau pour stocker tous les éléments tandis que HashMap
utilise Entry
, de sorte que SparseArray
coûte beaucoup moins cher en mémoire qu’un HashMap
, voir this
Il est regrettable que le compilateur envoie un avertissement. Je suppose que HashMap a été largement utilisé pour stocker des éléments.
Les SparseArrays ont leur place. Étant donné qu’ils utilisent un algorithme de recherche binaire pour trouver une valeur dans un tableau, vous devez tenir compte de ce que vous faites. La recherche binaire est O (log n) tandis que la recherche de hachage est O (1). Cela ne signifie pas nécessairement que la recherche binaire est plus lente pour un ensemble de données donné. Cependant, à mesure que le nombre d'entrées augmente, la puissance de la table de hachage prend le dessus. D'où les commentaires où un faible nombre d'entrées peut être égal et éventuellement meilleur que d'utiliser un HashMap.
Un hashmap est aussi bon que le hachage et peut également être affecté par le facteur de charge (je pense que dans les versions ultérieures, ils ignorent le facteur de charge afin qu'il puisse être mieux optimisé). Ils ont également ajouté un hash secondaire pour s'assurer que le hash est bon. La raison pour laquelle SparseArray fonctionne également très bien pour relativement peu d'entrées (<100).
Je suggérerais que si vous avez besoin d'une table de hachage et souhaitez une meilleure utilisation de la mémoire pour les entiers primitifs (pas de boxe automatique), etc., essayez-le. ( http://trove.starlight-systems.com - licence LGPL). (Aucune affiliation avec trésor, tout comme leur bibliothèque)
Avec le bâtiment multi-dex simplifié, nous n’avons même pas besoin de reconditionner ce que vous avez besoin. (trove a beaucoup de classes)