28 votes

Surcharge mémoire de Java HashMap par rapport à ArrayList

Je me demande quelle est la surcharge de la mémoire de java HashMap par rapport à ArrayList?

Mise à jour:

Je voudrais améliorer la vitesse de recherche pour des valeurs spécifiques d'un big pack (6 Millions+) des objets identiques.

Donc, je pense à propos de l'utilisation d'un ou plusieurs HashMap au lieu d'utiliser ArrayList. Mais je me demande qu'est-ce que la surcharge de la table de hachage.

Comme je le comprends, la clé n'est pas, seul le hachage de la clé, de sorte qu'il devrait être quelque chose comme la taille de la table de hachage de l'objet + un pointeur.

Mais qu'en fonction de hachage est utilisée? Est-il celui qui est offert par un Objet ou un autre?

32voto

Tim Cooper Points 2481

Si vous êtes en comparant HashMap avec ArrayList, je présume que vous êtes en train de faire une sorte de recherche/indexation de la liste de tableaux, tels que les binaires de recherche ou de la coutume de la table de hachage...? Parce que une .get(clé) à 6 millions d'entrées serait impossible à l'aide d'une recherche linéaire.

À l'aide de cette hypothèse, j'ai fait quelques tests empiriques et arriver à la conclusion que "Vous pouvez stocker 2,5 fois plus petits objets dans la même quantité de RAM si vous utilisez ArrayList avec les binaires de recherche ou de la coutume de hachage de la carte de mise en œuvre, par rapport à table de hachage". Mon test était basé sur les petits objets ne contenant que 3 champs, dont l'une est la clé, et la clé est un nombre entier. J'ai utilisé un 32 bits jdk 1.6. Voir ci-dessous pour les mises en garde sur cette figure de la "2.5".

Les principaux éléments à noter sont:

(a) il n'est pas l'espace requis pour les références ou "facteur de charge" qui vous tue, mais plutôt les frais généraux nécessaires pour la création d'objet. Si la clé est un type primitif, ou une combinaison de 2 ou plusieurs primitives ou de valeurs de référence, puis chaque clé aura besoin de son propre objet, qui transporte une charge de 8 octets.

(b) Dans mon expérience, vous devez généralement la clé, comme une partie de la valeur, (par exemple pour stocker les enregistrements de client, indexé par le numéro de client, vous voulez toujours l'id client dans le cadre de l'objet Client). Cela signifie qu'il est de l'OMI, un peu de gaspillage qu'une HashMap séparément stocke les références de clés et de valeurs.

Mises en garde:

  1. Le type le plus commun utilisé pour HashMap clés de la Chaîne. La création de l'objet de frais généraux ne s'applique pas ici, donc, la différence serait moindre.

  2. J'ai obtenu un chiffre de 2,8, étant 8880502 entrées inséré dans la liste de tableaux en comparaison avec 3148004 dans la table de hachage sur -Xmx256M JVM, mais mon ArrayList facteur de charge est de 80% et mes objets étaient assez petites - 12 octets 8 octets de l'objet de frais généraux.

  3. Ma figure et ma mise en œuvre, nécessite que la clé est contenue dans la valeur, sinon j'aurais le même problème avec la création d'un objet de frais généraux et ce serait juste une autre mise en œuvre de la table de hachage.

Mon code:

public class Payload {
    int key,b,c;
    Payload(int _key) { key = _key; }
}


import org.junit.Test;

import java.util.HashMap;
import java.util.Map;


public class Overhead {
    @Test
    public void useHashMap()
    {
        int i=0;
        try {
            Map<Integer, Payload> map = new HashMap<Integer, Payload>();
            for (i=0; i < 4000000; i++) {
                int key = (int)(Math.random() * Integer.MAX_VALUE);
                map.put(key, new Payload(key));
            }
        }
        catch (OutOfMemoryError e) {
            System.out.println("Got up to: " + i);
        }
    }

    @Test
    public void useArrayList()
    {
        int i=0;
        try {
            ArrayListMap map = new ArrayListMap();
            for (i=0; i < 9000000; i++) {
                int key = (int)(Math.random() * Integer.MAX_VALUE);
                map.put(key, new Payload(key));
            }
        }
        catch (OutOfMemoryError e) {
            System.out.println("Got up to: " + i);
        }
    }
}


import java.util.ArrayList;


public class ArrayListMap {
    private ArrayList<Payload> map = new ArrayList<Payload>();
    private int[] primes = new int[128];

    static boolean isPrime(int n)
    {
        for (int i=(int)Math.sqrt(n); i >= 2; i--) {
            if (n % i == 0)
                return false;
        }
        return true;
    }

    ArrayListMap()
    {
        for (int i=0; i < 11000000; i++)    // this is clumsy, I admit
            map.add(null);
        int n=31;
        for (int i=0; i < 128; i++) {
            while (! isPrime(n))
                n+=2;
            primes[i] = n;
            n += 2;
        }
        System.out.println("Capacity = " + map.size());
    }

    public void put(int key, Payload value)
    {
        int hash = key % map.size();
        int hash2 = primes[key % primes.length];
        if (hash < 0)
            hash += map.size();
        do {
            if (map.get(hash) == null) {
                map.set(hash, value);
                return;
            }
            hash += hash2;
            if (hash >= map.size())
                hash -= map.size();
        } while (true);
    }

    public Payload get(int key)
    {
        int hash = key % map.size();
        int hash2 = primes[key % primes.length];
        if (hash < 0)
            hash += map.size();
        do {
            Payload payload = map.get(hash);
            if (payload == null)
                return null;
            if (payload.key == key)
                return payload;
            hash += hash2;
            if (hash >= map.size())
                hash -= map.size();
        } while (true);
    }
}

12voto

Jon Skeet Points 692016

La chose la plus simple serait de regarder la source et de travailler ainsi. Cependant, vous comparez vraiment des pommes et des oranges - les listes et les cartes sont conceptuellement assez distinctes. Il est rare que vous choisissiez entre eux en fonction de l'utilisation de la mémoire.

Quel est le contexte derrière cette question?

8voto

Bill K Points 32115

Tout ce qui est stocké dans l'un des deux est des pointeurs. En fonction de votre architecture d'un pointeur devrait être de 32 ou 64 bits (ou plus ou moins)

Un tableau de la liste des 10 tend à affecter 10 "Pointeurs" au minimum (et aussi un peu d'une surcharge de temps stuff).

Une carte a allouer deux fois (20 pointeurs) car elle conserve les deux valeurs à la fois. Puis pour couronner le tout, il doit stocker le "Hash". qui devrait être plus grande que la carte, à une charge de 75%, il DEVRAIT être d'environ 13 valeurs de 32 bits (empreintes).

donc, si vous voulez une réponse désinvolte, le ratio devrait être d'environ 1:3.25, mais vous ne parlons pointeur de stockage, de très petites, sauf si vous stockez un grand nombre d'objets, et, dans l'affirmative, l'utilité d'être en mesure de référence instantanément (HashMap) vs iterate (array) devrait être BEAUCOUP plus importante que la taille de la mémoire.

Ah, aussi: Les tableaux peuvent être adaptés à la taille exacte de votre collection. HashMaps pouvez ainsi, si vous spécifiez la taille, mais si elle "Pousse" au-delà de cette taille, il sera ré-allouer un ensemble plus grand et de ne pas en utiliser certains, donc il peut y avoir un peu de déchets là-bas aussi.

5voto

sanscore Points 300

Je n'ai pas de réponse pour vous, mais une rapide recherche sur google monté une fonction en Java qui pourrait vous aider.

Moment de l'exécution.getRuntime().freeMemory();

Donc je vous propose de remplir une table de hachage et une liste de tableaux avec les mêmes données. Enregistrement de la mémoire libre, supprimer le premier objet, enregistrement de la mémoire, supprimer le deuxième objet, enregistrement de la mémoire, de calculer les différences,..., le profit!!!

Vous devriez faire ceci avec des magnitudes de données. ie Commencer avec 1000, 10000, 100000, 1000000.

EDIT: Corrigé, merci à amischiefr.

EDIT: Désolé pour l'édition de ton post, mais c'est assez important si vous allez utiliser cela (et C'est un peu beaucoup pour un commentaire) . freeMemory ne fonctionne pas comme vous pensez qu'il serait. Tout d'abord, sa valeur est modifiée par le garbage collection. Deuxièmement, c'est une valeur est modifiée lorsque java alloue plus de mémoire. Juste à l'aide de la freeMemory appel à elle seule ne fournit pas de données utiles.

Essayez ceci:

public static void displayMemory() {
    Runtime r=Runtime.getRuntime();
    r.gc();
    r.gc(); // YES, you NEED 2!
    System.out.println("Memory Used="+(r.totalMemory()-r.freeMemory()));
}

Ou vous pouvez revenir à la mémoire utilisée et le stocker, puis de le comparer à une valeur ultérieure. De toute façon, n'oubliez pas les 2 cg et la soustraction de totalMemory().

Encore une fois, désolé pour éditer ton post!

3voto

reccles Points 2282

Hashmaps essayer de maintenir un facteur de charge (généralement 75%), vous pouvez penser à une table de hachage comme un peu remplis tableau liste. Le problème, en ligne droite jusqu'comparaison est la taille de ce facteur de charge de la carte se développe pour répondre à la taille des données. Liste de tableaux sur l'autre main se développe pour répondre à ce besoin en le doublant interne de la taille de la matrice. De relativement petites tailles, elles sont comparables, cependant, comme vous le pack de plus en plus de données dans la carte, il nécessite beaucoup de vide de références afin de maintenir la valeur de hachage de la performance.

Dans les deux cas je recommande amorçage de la taille attendue des données avant de commencer à ajouter. Cela donnera à la mise en œuvre d'un meilleur réglage initial et sera susceptible de consommer moins d'-dessus de tous dans les deux cas.

Mise à jour:

basé sur la mise à jour de votre problème, consultez Vitrage listes. C'est un joli petit outil écrit par certaines des personnes chez Google pour faire des opérations similaires à celui que vous décrivez. Il est aussi très rapide. Permet de clustering, le filtrage, la recherche, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X