web-dev-qa-db-fra.com

Performances de Hashmap vs Array

Est-il préférable (en termes de performances) d'utiliser des tableaux ou des tables de hachage lorsque les index du tableau sont connus? Gardez à l'esprit que le "tableau d'objets/carte" de cet exemple n'est qu'un exemple. Dans mon projet réel, il est généré par une autre classe. Je ne peux donc pas utiliser de variables individuelles.

ArrayExample:

SomeObject[] objects = new SomeObject[2];
objects[0] = new SomeObject("Obj1");
objects[1] = new SomeObject("Obj2");

void doSomethingToObject(String Identifier){
    SomeObject object;
    if(Identifier.equals("Obj1")){
        object=objects[0];
    }else if(){
        object=objects[1];
    }
    //do stuff
}

HashMapExample:

HashMap objects = HashMap();
objects.put("Obj1",new SomeObject());
objects.put("Obj2",new SomeObject());

void doSomethingToObject(String Identifier){
    SomeObject object = (SomeObject) objects.get(Identifier);
    //do stuff
}

Le HashMap one semble beaucoup mieux, mais j’ai vraiment besoin de performance pour cela, alors c’est prioritaire.

EDIT: Well Array, alors, les suggestions sont toujours les bienvenues

EDIT: J'ai oublié de mentionner, la taille du tableau/HashMap est toujours la même (6)

EDIT: Il semble que les HashMaps sont plus rapides Tableau: 128ms Hash: 103ms.

En utilisant moins de cycles, HashMaps était encore deux fois plus rapide

code de test:

import Java.util.HashMap;
import Java.util.Random;

public class Optimizationsest {
private static Random r = new Random();

private static HashMap<String,SomeObject> hm = new HashMap<String,SomeObject>();
private static SomeObject[] o = new SomeObject[6];

private static String[] Indentifiers = {"Obj1","Obj2","Obj3","Obj4","Obj5","Obj6"};

private static int t = 1000000;

public static void main(String[] args){
    CreateHash();
    CreateArray();
    long loopTime = ProcessArray();
    long hashTime = ProcessHash();
    System.out.println("Array: " + loopTime + "ms");
    System.out.println("Hash: " + hashTime + "ms");
}

public static void CreateHash(){
    for(int i=0; i <= 5; i++){
        hm.put("Obj"+(i+1), new SomeObject());
    }
}

public static void CreateArray(){
    for(int i=0; i <= 5; i++){
        o[i]=new SomeObject();
    }
}

public static long ProcessArray(){
    StopWatch sw = new StopWatch();
    sw.start();
    for(int i = 1;i<=t;i++){
        checkArray(Indentifiers[r.nextInt(6)]);
    }
    sw.stop();
    return sw.getElapsedTime();
}



private static void checkArray(String Identifier) {
    SomeObject object;
    if(Identifier.equals("Obj1")){
        object=o[0];
    }else if(Identifier.equals("Obj2")){
        object=o[1];
    }else if(Identifier.equals("Obj3")){
        object=o[2];
    }else if(Identifier.equals("Obj4")){
        object=o[3];
    }else if(Identifier.equals("Obj5")){
        object=o[4];
    }else if(Identifier.equals("Obj6")){
        object=o[5];
    }else{
        object = new SomeObject();
    }
    object.kill();
}

public static long ProcessHash(){
    StopWatch sw = new StopWatch();
    sw.start();
    for(int i = 1;i<=t;i++){
        checkHash(Indentifiers[r.nextInt(6)]);
    }
    sw.stop();
    return sw.getElapsedTime();
}

private static void checkHash(String Identifier) {
    SomeObject object = (SomeObject) hm.get(Identifier);
    object.kill();
}

}

17
Tim

HashMap utilise un tableau en dessous, il ne peut donc jamais être plus rapide que de l'utiliser correctement.

Random.nextInt() est plusieurs fois plus lent que ce que vous testez, même utiliser un tableau pour tester un tableau va biaiser vos résultats. 

La raison pour laquelle votre repère de tableau est si lent est due aux comparaisons d'égaux, pas à l'accès au tableau lui-même.

HashTable est généralement beaucoup plus lent que HashMap car il fait à peu près la même chose mais est également synchronisé.

Le problème commun aux micro-benchmarks est le JIT qui est très efficace pour supprimer du code qui ne fait rien. Si vous ne faites pas attention, vous ne ferez que vérifier si vous avez assez confondu le JIT pour qu'il ne puisse pas fonctionner, votre code ne fait rien.

C'est l'une des raisons pour lesquelles vous pouvez écrire des micro-points de repère qui surpassent les systèmes C++. En effet, Java est un langage plus simple et plus facile à raisonner et donc à détecter du code qui ne sert à rien. Cela peut conduire à des tests qui montrent que Java ne fait "rien d’utile" beaucoup plus rapidement que C++;)

30
Peter Lawrey

les tableaux lorsque les index sont connus sont plus rapides (HashMap utilise un tableau de listes chaînées en arrière-plan, ce qui ajoute un peu de surcharge au-dessus des accès au tableau, sans oublier les opérations de hachage à effectuer)

et FYI HashMap<String,SomeObject> objects = HashMap<String,SomeObject>(); fait en sorte que vous n'aurez pas à lancer

4
ratchet freak

Pour l'exemple présenté, HashTable gagne, je crois. Le problème avec l'approche par tableaux est qu'elle ne s'adapte pas. J'imagine que vous voulez avoir plus de deux entrées dans la table et que l'arbre de branche de condition dans doSomethingToObject sera rapidement lent et maniable.

2
sehe

Logiquement, HashMap convient parfaitement à votre cas. Du point de vue des performances, la solution est également gagnante car dans le cas de tableaux, vous devrez faire un certain nombre de comparaisons de chaînes (dans votre algorithme) alors que dans HashMap, vous utilisez simplement un code de hachage si le facteur de charge n’est pas trop élevé. Le tableau et HashMap devront être redimensionnés si vous ajoutez de nombreux éléments, mais dans le cas de HashMap, vous devrez également redistribuer les éléments. Dans ce cas d'utilisation, HashMap perd.

2
Alex Gitelman

S'il vous plaît, ne jamais, jamais utiliser prolongé if/else si/else si/else si/else si/else si cas comme ça. La raison pour laquelle je l'ai répété tant de fois, c'est simplement pour que vous vous sentiez comme votre interprète Java le fait quand il frappe des blocs de code comme celui-ci.

Dès que vous en avez plus d'un, utilisez soit un hashmap, soit un switch/case (Java 7 vous permettra de le faire sur Strings, et Java 6 vous devrez utiliser un enum). Une solution encore meilleure pour la vérification en lecture seule est une ImmutableMap d'un framework tel que goyave; ils ont des lectures hautement optimisées car ils n'autorisent pas les écritures.

0
Ajax

Les tableaux seront généralement plus rapides que les classes Collections.

PS. Vous avez mentionné HashTable dans votre message. HashTable a des performances encore pires que HashMap. Je suppose que votre mention de HashTable était une faute de frappe

"Le HashTable ressemble beaucoup Mieux"

0
Basanth Roy

L'exemple est étrange. Le problème clé est de savoir si vos données sont dynamiques. Si c'est le cas, vous ne pourriez pas écrire votre programme de cette façon (comme dans le cas d'un tableau). En d'autres termes, comparer entre votre implémentation de tableau et celle de hachage n'est pas juste. L'implémentation de hachage fonctionne pour les données dynamiques, mais pas celle du tableau. 

Si vous ne disposez que de données statiques (6 objets fixes), array ou hash ne fonctionne que comme détenteur de données. Vous pouvez même définir des objets statiques.

0
BlueGuitar