Java.util.Performances de la méthode Map values ()


J'ai une carte avec plusieurs millions d'entrées:

private final Map<String, SomeItem> tops = new HashMap<>();

J'ai besoin d'obtenir une liste de valeurs, ce qui pourrait être fait en appelant la méthode java.util.Map values().

La question :

Une collection de valeurs est créée à chaque fois que j'appelle cette méthode ou qu'elle est pré-calculée et qu'elle est sauvegardée du point de vue des performances pour l'appeler plusieurs fois ?

Le problème est que dans mon cas, Map a plusieurs millions d'éléments et je ne veux pas créer une nouvelle liste à chaque fois values() est appelé

Author: Aliaksei Stadnik, 2018-05-05

4 answers

Ci-dessous est l'implémentation copiée de Map.values() dans java.util.HashMap:

public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

Cela montre clairement que la collection de valeurs n'est pas créée sauf si nécessaire. Ainsi, il ne devrait pas y avoir de surcharge supplémentaire causée par les appels à values()

 3
Author: ernest_k, 2018-05-05 15:14:48

Un point important ici peut être: Cela n'a pas d'importance!


Mais d'abord, en se référant aux autres réponses jusqu'à présent: La collection qui y est retournée est généralement "mise en cache", en ce sens qu'elle est créée paresseusement, et ensuite, la même instance sera renvoyée. Par exemple, en considérant l'implémentation dans la classe HashMap:

public Collection<V> values() {
    Collection<V> vs;
    return (vs = values) == null ? (values = new Values()) : vs;
}

Cela est même spécifié (dans le cadre du contrat, en tant que spécification de mise en œuvre) dans la documentation du AbstractMap classe (sur laquelle la plupart des implémentations Map sont basées):

La collection est créée la première fois que cette méthode est appelée, et renvoyée en réponse à tous les appels suivants. Aucune synchronisation n'est effectuée, il y a donc une légère chance que plusieurs appels à cette méthode ne renvoient pas tous la même collection.


Mais maintenant, on pourrait soutenir que la mise en œuvre pourrait changer plus tard. L'implémentation de la classe HashMap pourrait changer, ou on pourrait passez à une autre implémentation Map qui n'étend pas AbstractMap et qui est implémentée différemment. Le fait qu'il soit actuellement implémenté comme ceci n'est (pour lui-même) aucune garantie qu'il sera toujours implémenté comme ceci.

Donc le point le plus important (et la raison pour laquelle cela n'a pas d'importance) est que la méthode values()est en effet censée renvoyer une vue de collection . Comme indiqué dans la documentation du Map interface :

L'interface de la carte fournit trois vues de collection, qui permettent de visualiser le contenu d'une carte comme un ensemble de clés, une collection de valeurs ou un ensemble de mappages clé-valeur.

Et plus précisément, la documentation de la méthode Map#values() :

Renvoie une vue de collection des valeurs contenues dans cette carte. La collection est adossée à la carte, de sorte que les modifications apportées à la carte sont reflétées dans la collection, et inversement.

Je ne peux pas imaginer un moyen raisonnable de mettre en œuvre une telle vue qui implique le traitement de toutes les valeurs du Map.


Ainsi, par exemple, imaginez que l'implémentation dans {[2] } était comme ceci:

public Collection<V> values() {
    return new Values();
}

Alors il retournerait une nouvelle collection chaque fois qu'il était appelé. Mais la création de cette collection ne pas du tout implique le traitement des valeurs.

Ou pour le dire ainsi: Le coût d'appeler cela la méthode est indépendant de de la taille de la carte. Il a essentiellement le coût d'une création d'objet unique, que la carte contienne 10 ou 10000 éléments.

 1
Author: Marco13, 2018-05-05 16:05:00

Comme d'autres l'ont mentionné, vous pouvez le voir en regardant le code. Vous pouvez également coder un exemple rapide pour vous le prouver. Le code ci-dessous imprimera true 10 fois car l'identité de l'objet sera toujours la même pour les valeurs.

public static void main(String[] args) {
    Map<String, String> myMap = new HashMap();
    Collection<String> lastValues = myMap.values();
    for (int i=0; i < 10; i++) {
        System.out.println(lastValues == myMap.values());
        lastValues = myMap.values();
    }
}

Le code suivant affichera true la première fois, puis false les 9 fois suivantes.

public static void main(String[] args) {
    Map<String, String> myMap = new HashMap();
    Collection<String> lastValues = myMap.values();
    for (int i=0; i < 10; i++) {
        System.out.println(lastValues == myMap.values());
        lastValues = myMap.values();
        myMap = new HashMap();
    }
}
 0
Author: Daryl Handley, 2018-05-05 15:38:49

Une autre suggestion après avoir lu ce fil, si le contenu déclaré des sommets de la carte n'est pas modifié, vous pouvez utiliser l'objet google guava ImmutableMap. Pour plus d'informations- UnmodifiableMap (Collections Java) vs ImmutableMap (Google)

 0
Author: ashish gupta, 2018-05-05 23:01:35