Avertissement FindBugs: utilisation inefficace de l'itérateur keySet au lieu de l'itérateur entrySet

s'il vous Plaît se référer à la méthode suivante :

public Set<LIMSGridCell> getCellsInColumn(String columnIndex){
    Map<String,LIMSGridCell> cellsMap = getCellsMap();
    Set<LIMSGridCell> cells = new HashSet<LIMSGridCell>();
    Set<String> keySet = cellsMap.keySet();
    for(String key: keySet){
      if(key.startsWith(columnIndex)){
        cells.add(cellsMap.get(key));
      }
    }
    return cells;
  }

FindBugs donner à cette waring message :

" utilisation inefficace de l'itérateur keySet au lieu de l'itérateur entrySet Cette méthode accède à la valeur D'une entrée Map, en utilisant une clé qui récupéré à partir d'un jeu de clés itérateur. Il est plus efficace d'utiliser un iterator sur l'entrée de la carte, pour éviter la carte.get(clé) Lookup."

28
demandé sur TryTryAgain 2012-09-28 15:36:11

5 réponses

vous récupérez toutes les clés (en accédant à toute la carte) et pour certaines clés, vous accédez de nouveau à la carte pour obtenir la valeur.

vous pouvez itérer sur la carte pour obtenir les entrées de la carte (Carte.L'entrée) (les couples de clés et de valeurs) et accéder à la carte qu'une seule fois.

Carte.entrySet () fournit un ensemble de Map.Entrys chacun avec la clé et la valeur correspondante.

for ( Map.Entry< String, LIMSGridCell > entry : cellsMap.entrySet() ) {
    if ( entry.getKey().startsWith( columnIndex ) ) {
        cells.add( entry.getValue() );
    }
}

Remarque:: je doute que ce soit une amélioration puisque si vous utilisez des entrées de carte vous instanciez un objet pour chaque entrée. Je ne sais pas si c'est vraiment plus rapide que d'appeler get() et extraire la référence nécessaire directement.

48
répondu Matteo 2015-03-03 05:34:59

vous obtenez l'ensemble des clés dans la carte, puis en utilisant chaque clé pour obtenir la valeur hors de la carte.

au lieu de cela, vous pouvez simplement itérer à travers le Carte.L'entrée les paires clé/valeur retournée par entrySet(). De cette façon, vous éviter relativement cher get() recherche (notez l'utilisation du mot relativement ici)

e.g.

for (Map.Entry<String,LIMSGridCell> e : map.entrySet()) {
   // do something with...
   e.getKey();
   e.getValue();
}
7
répondu Brian Agnew 2012-09-28 11:38:45

si quelqu'un est toujours intéressé par une réponse détaillée et numérotée: oui, vous devez utiliser entrySet() vs. keySet() dans le cas où vous êtes itération sur l'ensemble de l' Carte. Voir ce Gist pour les numéros détaillés. J'exécute un benchmark avec JMH pour les implémentations par défaut de Map avec Oracle JDK8.

La principale conclusion est: il est toujours un peu plus lent à itérer sur les keySet et re-requête pour chaque touche. Dès que vous avez plus de cartes, le multiplicateur peut devenir assez volumineux (par exemple,ConcurrentSkipListMap c'est toujours 5-10x; tandis que pour HashMaps, il n'est pas plus grand que 2x jusqu'à un million d'entrées).

cependant, il s'agit encore de très petits nombres. Le moyen le plus lent pour itérer plus de 1 million d'entrées est avec un ConcurrentSkipListMap.keySet(), qui est d'environ 500-700 millisecondes; tout en itérant sur plus de IdentityHashMap.entrySet() est seulement 25-30 millisecondes avec LinkedHashMap.entrySet() juste derrière avec 40-50 millisecondes (pas surprenant, car il a un LinkedList à l'intérieur, ce qui contribue à itération.) Comme une vue d'ensemble de la ci-dessus liés Résumé:

Map type              | Access Type | Δ for 1M entries
----------------------+-------------+-----------------
HashMap               | .entrySet() |     69-72  ms
HashMap               |   .keySet() |     86-94  ms
ConcurrentHashMap     | .entrySet() |     72-76  ms
ConcurrentHashMap     |   .keySet() |     87-95  ms
TreeMap               | .entrySet() |    101-105 ms
TreeMap               |   .keySet() |    257-279 ms
LinkedHashMap         | .entrySet() |     37-49  ms
LinkedHashMap         |   .keySet() |     89-120 ms
ConcurrentSkipListMap | .entrySet() |     94-108 ms
ConcurrentSkipListMap |   .keySet() |    494-696 ms
IdentityHashMap       | .entrySet() |     26-29  ms
IdentityHashMap       |   .keySet() |     69-77  ms

donc le résultat est: cela dépend de votre cas d'utilisation. Alors qu'il est certainement plus vite pour itérer sur les entrySet() Les chiffres ne sont pas énormes, Surtout pour les cartes relativement petites. Cependant, si vous itérez sur une carte avec 1 million d'entrées assez régulièrement, mieux vaut utiliser la manière plus rapide;)

les nombres, bien sûr, sont juste pour comparer les uns avec les autres, pas absolues.

7
répondu D. Kovács 2018-05-11 06:30:51

C'est la suggestion; pas vraiment une réponse à votre question. Lorsque vous travaillez avec Competienthashmap, vous trouverez ci-dessous le comportement de l'itérateur mentionné dans javadoc

l'itérateur de la vue est un itérateur "faiblement cohérent" qui ne sera jamais lancer ConcurrentModificationException, et des garanties de traverser les éléments tels qu'ils existaient lors de la construction de l'itérateur, et peuvent (mais il n'est pas garanti) tenir compte de toute modification ultérieure à construction.

donc si vous utilisez EntrySet iterator; cela peut contenir une paire clé/valeur périmée; donc ce serait mieux; obtenez la clé de keySet iterator(); et vérifiez avec collection pour la valeur. cela vous assurera que vous recevez le changement récent de la collection.

si vous êtes D'accord avec l'itérateur de sécurité intégrée; alors vérifiez ceci lien; il indique l'utilisation d'entrySet; peu d'amélioration de la performance.

2
répondu Kanagavelu Sugumar 2014-06-26 08:21:35

dans keyset, vous devez obtenir toutes les clés et ensuite vous recherchez chaque clé dans la collection.

de plus, la boucle au-dessus de l'entrySet est plus rapide, parce que vous n'interrogez pas la carte deux fois pour chaque clé.

si vous n'avez besoin que de clés ou de valeurs de votre carte, utilisez plutôt keySet() ou values().

0
répondu Chetan Laddha 2015-05-14 06:54:32