Cyril Mottier

“It’s the little details that are vital. Little things make big things happen.” – John Wooden

Astuce #5 : Stop Aux HashMaps!

Voilà un bon moment que je n'ai pas rédigé d'article sur ce blog. La raison est assez simple et n'est absolument pas dûe à un manque de créativité (loin de là … là dessus je n'ai pour l'instant aucun problème : les idées me sautent sans arrêt à l'esprit) ou tout simplement la feignantise … non le problème est beaucoup plus sérieux puisqu'il s'agit d'un simple mais non moins terrible manque de temps ! Beaucoup auront compris pourquoi : je veux faire trop de choses mais je n'arrive pas à me dédoubler ou mieux me n-doubler !

Après un article plus que complet concernant le tramage sous Android, je souhaitais repartir sur un sujet plus classique et plus succinct traitant d'optimisation. Lorsqu'on apprend le Java, on est rarement confronté aux problèmes de performances puisqu'on apprend, en général, sur un ordinateur. Malheureusement lorsqu'on applique purement et simplement ces enseignements dans le monde du mobile et notamment sur Android, on se retrouve souvent confronté à des problèmes de performances. Le fameux GC n'est pas loin …

L'exemple le plus flagrant est celui de l'utilisation des objets de type “dictionnaire” qui enregistrent les valeurs sous la forme de paires (clés, valeurs). Cette classe, souvent extrêmement utile et irremplaçable porte le nom de HashMap<K, V>. L'utilisation d'une telle classe implique malheureusement d'utiliser des objets comme clé ET comme valeur. Cela signifie que si vous souhaitez avoir une HashMap<int, String> vous aurez en fin de compte une HashMap<Integer, String> dans laquelle le type Integer est un objet qui contient un unique int (méthode du boxing).

Etant multi-compétence, Android et iPhone, je rencontre souvent des gens qui découvrent le monde Android et n'hésite pas à faire des HashMap un peu partout en lieu et place des traditionnels NSDictionnary. Il s'avère que la programmation sous iPhone est très différente puisque le choix de la classe à utiliser est très restreint : NSArray pour un tableau, NSDictionnary pour un dictionnaire, etc. En réalité, développer sous iPhoneOS avec des NSDictionnary ne pose pas autant de problème puisqu'il n'y a pas de notion de GC (la ressource est libérée instantanément). En conséquence le boxing/unboxing ne coûte relativement rien.

Prenons MetroMap à titre d'exemple : cette application contient, vous vous en douter, un cache de Bitmaps. Ce dernier associe simplement des Bitmap (valeurs) à des int (clé représentant l'identifiant du Drawable - R.drawable.XXX - utilisé pour la tuile). Pour éviter la création inutile d'objets de type Integer j'ai donc intelligemment (bah oui je fais pas des trucs débiles !) préféré utiliser la classe SparseArray :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.cyrilmottier.android.metromap.adapter;

import java.lang.ref.SoftReference;

import android.util.SparseArray;

public class EfficientCache<T> implements Cache<T> {

    private SparseArray<SoftReference<T>> mCache;

    public EfficientCache(int cacheSize) {
        mCache = new SparseArray<SoftReference<T>>(cacheSize);
    }

    public T get(int key) {
        final SoftReference<T> softRef = mCache.get(key);
        return (softRef == null) ? null : softRef.get();
    }

    public void put(int key, T element) {
        mCache.put(key, new SoftReference<T>(element));
    }

}

Cette classe vous permet d'éviter le surcoût de création d'objets inutiles (les clés) à moindre “frais”. Lorsque cela est possible (c'est à dire lorsque vos clés sont des int, je vous conseille vivement de préférer cette classe à la basique et traditionnelle HashMap<K, V> Le framework Android fournit dans android.util, un ensemble de 3 classes qui peuvent être utilisées en lieu et place d'HashMap<K, V>:

  • SparseArray permet de mapper des objets avec des int. Cela revient donc à un “vrai” HashMap<int, V>

  • SparseIntArray : à l'instar de SparseArray, cette classe fait correspondre des int avec des int. C'est donc similaire (en terme de fonctionnalités) à HashMap<int, int>

  • SparseBooleanArray : Revient à HashMap<int, boolean>

A votre tour d'optimiser vos programmes et de supprimer les HashMaps inutiles.