over - parcourir map java 8




Comment effectuer une itération efficace sur chaque entrée d'une carte Java? (20)

Expression Lambda Java 8

En Java 1.8 (Java 8), cela est devenu beaucoup plus facile en utilisant la méthode forEach des opérations Aggregate ( opérations Stream ) qui ressemble à des itérateurs de Iterable Interface.

Il vous suffit de copier coller l'instruction ci-dessous dans votre code et de renommer la variable HashMap de hm en votre variable HashMap pour imprimer la paire clé-valeur.

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
 *     Logic to put the Key,Value pair in your HashMap hm
 */

// Print the key value pair in one line.

hm.forEach((k,v) -> System.out.println("key: "+k+" value:"+v));

// Just copy and paste above line to your code.

Voici l'exemple de code que j'ai essayé d'utiliser l' expression lambda . Ce truc est tellement cool. Dois essayer.

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
    Random rand = new Random(47);
    int i=0;
    while(i<5){
        i++;
        int key = rand.nextInt(20);
        int value = rand.nextInt(50);
        System.out.println("Inserting key: "+key+" Value: "+value);
        Integer imap =hm.put(key,value);
        if( imap == null){
            System.out.println("Inserted");
        }
        else{
            System.out.println("Replaced with "+imap);
        }               
    }

    hm.forEach((k,v) -> System.out.println("key: "+k+" value:"+v));

Output:

Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11

On peut aussi utiliser Spliterator pour la même chose.

Spliterator sit = hm.entrySet().spliterator();

METTRE À JOUR

Y compris les liens vers la documentation Oracle Docs. Pour plus d'informations sur Lambda, accédez à ce link et devez lire Opérations d'agrégat. Pour Spliterator, accédez à ce link .

Si j’ai un objet implémentant l’interface Map en Java et que je souhaite parcourir toutes les paires qu’il contient, quel est le moyen le plus efficace de parcourir la carte?

La commande des éléments dépendra-t-elle de la mise en oeuvre de carte spécifique que j'ai pour l'interface?


Avec Eclipse Collections (anciennement GS Collections ), vous utiliseriez la méthode forEachKeyValue sur l'interface MapIterable , héritée des interfaces MutableMap et ImmutableMap et de leurs implémentations.

final MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue(new Procedure2<Integer, String>()
{
    public void value(Integer key, String value)
    {
        result.add(key + value);
    }
});
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Avec la syntaxe Java 8 lambda, vous pouvez écrire le code comme suit:

MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue((key, value) -> result.add(key + value));
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Remarque: je suis un partisan des collections Eclipse.


C'est une question en deux parties:

Comment parcourir les entrées d'une carte - @ ScArcher2 a parfaitement answered cette question.

Quel est l'ordre des itérations - si vous utilisez uniquement Map , à proprement parler, il n'y a aucune garantie de commande . Donc, vous ne devriez pas vraiment vous fier à l'ordre donné par une implémentation quelconque. Cependant, l'interface SortedMap étend Map et fournit exactement ce que vous recherchez - les implémentations donneront toujours un ordre de tri cohérent.

NavigableMap est une autre extension utile . Il s'agit d'une SortedMap avec des méthodes supplémentaires pour rechercher des entrées en fonction de leur position dans le jeu de clés. Donc, potentiellement, cela peut éliminer le besoin de procéder à une itération en premier lieu - vous pourrez peut-être trouver l' entry spécifique que vous utilisez après avoir utilisé les higherEntry , lowerEntry , ceilingEntry ou floorEntry . La méthode descendingMap vous donne même une méthode explicite d’ inverser l’ordre de traversée .


Dans Map, on peut modifier les keys et / ou les values et / ou les both (eg, entrySet) fonction de l’intérêt de _ Like:

1.) Parcourez les keys -> keySet() de la carte:

Map<String, Object> map = ...;

for (String key : map.keySet()) {
    //your Business logic...
}

2.) Parcourez les values -> values() de la carte:

for (Object value : map.values()) {
    //your Business logic...
}

3.) Parcourez les both -> entrySet() de la carte:

for (Map.Entry<String, Object> entry : map.entrySet()) {
    String key = entry.getKey();
    Object value = entry.getValue();
    //your Business logic...
}

De plus, il existe 3 manières différentes de parcourir un HashMap. Ils sont comme ci-dessous _

//1.
for (Map.Entry entry : hm.entrySet()) {
    System.out.print("key,val: ");
    System.out.println(entry.getKey() + "," + entry.getValue());
}

//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
    Integer key = (Integer)iter.next();
    String val = (String)hm.get(key);
    System.out.println("key,val: " + key + "," + val);
}

//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    Integer key = (Integer)entry.getKey();
    String val = (String)entry.getValue();
    System.out.println("key,val: " + key + "," + val);
}

En Java 8, vous pouvez le faire rapidement et proprement en utilisant les nouvelles fonctionnalités de lambdas:

 Map<String,String> map = new HashMap<>();
 map.put("SomeKey", "SomeValue");
 map.forEach( (k,v) -> [do something with key and value] );

 // such as
 map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));

Le type de k et v sera déduit par le compilateur et il n'y Map.Entry plus besoin d'utiliser Map.Entry .

Peasy facile!


En théorie, le moyen le plus efficace dépend de quelle implémentation de Map. Pour ce faire, la méthode officielle consiste à appeler map.entrySet() , qui renvoie un ensemble de Map.Entry , chacun contenant une clé et une valeur ( entry.getKey() et entry.getValue() ).

Dans une implémentation idiosyncratique, l'utilisation de map.keySet() , map.entrySet() ou autre peut faire une différence. Mais je ne peux pas penser à une raison pour laquelle quelqu'un voudrait l'écrire comme ça. Très probablement, ce que vous faites ne fait aucune différence quant aux performances.

Et oui, l'ordre dépendra de la mise en œuvre - ainsi que (éventuellement) de l'ordre d'insertion et d'autres facteurs difficiles à contrôler.

[edit] J'ai écrit valueSet() origine, mais bien sûr, entrySet() est en fait la réponse.


Exemple d'utilisation d'itérateur et de génériques:

Iterator<Map.Entry<String, String>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Map.Entry<String, String> entry = entries.next();
  String key = entry.getKey();
  String value = entry.getValue();
  // ...
}

Il y a beaucoup de façons de le faire. Voici quelques étapes simples:

Supposons que vous ayez une carte comme:

Map<String, Integer> m = new HashMap<String, Integer>();

Ensuite, vous pouvez faire quelque chose comme ci-dessous pour parcourir les éléments de la carte.

// ********** Using an iterator ****************
Iterator<Entry<String, Integer>> me = m.entrySet().iterator();
while(me.hasNext()){
    Entry<String, Integer> pair = me.next();
    System.out.println(pair.getKey() + ":" + pair.getValue());
}

// *********** Using foreach ************************
for(Entry<String, Integer> me : m.entrySet()){
    System.out.println(me.getKey() + " : " + me.getValue());
}

// *********** Using keySet *****************************
for(String s : m.keySet()){
    System.out.println(s + " : " + m.get(s));
}

// *********** Using keySet and iterator *****************
Iterator<String> me = m.keySet().iterator();
while(me.hasNext()){
    String key = me.next();
    System.out.println(key + " : " + m.get(key));
}

Le classement dépendra toujours de l’implémentation spécifique de la carte. En utilisant Java 8, vous pouvez utiliser l’un des deux suivants:

map.forEach((k,v) -> { System.out.println(k + ":" + v); });

Ou:

map.entrySet().forEach((e) -> {
            System.out.println(e.getKey() + " : " + e.getValue());
        });

Le résultat sera le même (même ordre). EntrySet soutenu par la carte afin que vous obteniez le même ordre. Le second est pratique car il vous permet d’utiliser lambdas, par exemple si vous souhaitez uniquement imprimer uniquement les objets Integer supérieurs à 5:

map.entrySet()
    .stream()
    .filter(e-> e.getValue() > 5)
    .forEach(System.out::println);

Le code ci-dessous montre l'itération via LinkedHashMap et HashMap normal (exemple). Vous verrez la différence dans la commande:

public class HMIteration {


    public static void main(String[] args) {
        Map<Object, Object> linkedHashMap = new LinkedHashMap<>();
        Map<Object, Object> hashMap = new HashMap<>();

        for (int i=10; i>=0; i--) {
            linkedHashMap.put(i, i);
            hashMap.put(i, i);
        }

        System.out.println("LinkedHashMap (1): ");
        linkedHashMap.forEach((k,v) -> { System.out.print(k + " (#="+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nLinkedHashMap (2): ");

        linkedHashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });


        System.out.println("\n\nHashMap (1): ");
        hashMap.forEach((k,v) -> { System.out.print(k + " (#:"+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nHashMap (2): ");

        hashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });
    }
}

LinkedHashMap (1):

10 (# = 10): 10, 9 (# = 9): 9, 8 (# = 8): 8, 7 (# = 7): 7, 6 (# = 6): 6, 5 (# = 5 ): 5, 4 (# = 4): 4, 3 (# = 3): 3, 2 (# = 2): 2, 1 (# = 1): 1, 0 (# = 0): 0,

LinkedHashMap (2):

10: 10, 9: 9, 8: 8, 7: 7, 6: 6, 5: 5, 4: 4, 3: 3, 2: 2, 1: 1, 0: 0,

HashMap (1):

0 (#: 0): 0, 1 (#: 1): 1, 2 (#: 2): 2, 3 (#: 3): 3, 4 (#: 4): 4, 5 (#: 5 ): 5, 6 (#: 6): 6, 7 (#: 7): 7, 8 (#: 8): 8, 9 (#: 9): 9, 10 (#: 10): 10,

HashMap (2):

0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10,


Le code typique pour itérer sur une carte est:

Map<String,Thing> map = ...;
for (Map.Entry<String,Thing> entry : map.entrySet()) {
    String key = entry.getKey();
    Thing thing = entry.getValue();
    ...
}

HashMap est l'implémentation canonique de la carte et ne donne aucune garantie (ou bien, elle ne devrait pas changer d'ordre si aucune opération de mutation n'est effectuée dessus). SortedMap renverra les entrées en fonction de l'ordre naturel des clés ou d'un Comparator , le cas échéant. LinkedHashMap renverra les entrées dans l'ordre d'insertion ou dans l'ordre d'accès, selon la manière dont il a été construit. EnumMap renvoie les entrées dans l'ordre naturel des clés.

(Mise à jour: je pense que ce n'est plus vrai. ) Remarque, l' entrySet IdentityHashMap a actuellement une implémentation particulière qui renvoie la même instance Map.Entry pour chaque élément du entrySet ! Cependant, chaque fois qu'un nouvel itérateur avance, Map.Entry est mis à jour.


Oui, comme beaucoup de gens l’ont convenu, c’est le meilleur moyen de parcourir une Map .

Mais il y a des chances de nullpointerexception si la carte est null . N'oubliez pas de mettre null .check in.

                                                 |
                                                 |
                                         - - - -
                                       |
                                       |
for (Map.Entry<String, Object> entry : map.entrySet()) {
    String key = entry.getKey();
    Object value = entry.getValue();
}

Oui, l'ordre dépend de l'implémentation spécifique de la carte.

@ ScArcher2 a la syntaxe Java 1.5 plus élégante . En 1.4, je ferais quelque chose comme ceci:

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}

Pour résumer les autres réponses et les combiner avec ce que je sais, j'ai trouvé 10 façons principales de le faire (voir ci-dessous). J'ai également écrit des tests de performance (voir les résultats ci-dessous). Par exemple, si nous voulons trouver la somme de toutes les clés et de toutes les valeurs d'une carte, nous pouvons écrire:

  1. Utiliser itérateur et Map.Entry

    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    
  2. Utiliser foreach et Map.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    
  3. Utilisation de forEach à partir de Java 8

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    
  4. Utiliser keySet et foreach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    
  5. Utilisation de keySet et d' itérateur

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
    
  6. Utiliser pour et Map.Entry

    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    
  7. Utilisation de l' API Java 8 Stream

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  8. Utilisation parallèle de l'API Java 8 Stream

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  9. Utilisation de Apache Collections IterableMap of Apache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
    
  10. Utilisation de collections MutableMap of Eclipse (CS)

    final long[] i = {0};
    mutableMap.forEachKeyValue((key, value) -> {
        i[0] += key + value;
    });
    

Tests de performances (mode = AverageTime, système = Windows 8.1 64 bits, Intel i7-4790 3.60 GHz, 16 Go)

  1. Pour une petite carte (100 éléments), le score 0,308 est le meilleur

    Benchmark                          Mode  Cnt  Score    Error  Units
    test3_UsingForEachAndJava8         avgt  10   0.308 ±  0.021  µs/op
    test10_UsingEclipseMap             avgt  10   0.309 ±  0.009  µs/op
    test1_UsingWhileAndMapEntry        avgt  10   0.380 ±  0.014  µs/op
    test6_UsingForAndIterator          avgt  10   0.387 ±  0.016  µs/op
    test2_UsingForEachAndMapEntry      avgt  10   0.391 ±  0.023  µs/op
    test7_UsingJava8StreamApi          avgt  10   0.510 ±  0.014  µs/op
    test9_UsingApacheIterableMap       avgt  10   0.524 ±  0.008  µs/op
    test4_UsingKeySetAndForEach        avgt  10   0.816 ±  0.026  µs/op
    test5_UsingKeySetAndIterator       avgt  10   0.863 ±  0.025  µs/op
    test8_UsingJava8StreamApiParallel  avgt  10   5.552 ±  0.185  µs/op
    
  2. Pour une carte avec 10000 éléments, le score 37.606 est le meilleur

    Benchmark                           Mode   Cnt  Score      Error   Units
    test10_UsingEclipseMap              avgt   10    37.606 ±   0.790  µs/op
    test3_UsingForEachAndJava8          avgt   10    50.368 ±   0.887  µs/op
    test6_UsingForAndIterator           avgt   10    50.332 ±   0.507  µs/op
    test2_UsingForEachAndMapEntry       avgt   10    51.406 ±   1.032  µs/op
    test1_UsingWhileAndMapEntry         avgt   10    52.538 ±   2.431  µs/op
    test7_UsingJava8StreamApi           avgt   10    54.464 ±   0.712  µs/op
    test4_UsingKeySetAndForEach         avgt   10    79.016 ±  25.345  µs/op
    test5_UsingKeySetAndIterator        avgt   10    91.105 ±  10.220  µs/op
    test8_UsingJava8StreamApiParallel   avgt   10   112.511 ±   0.365  µs/op
    test9_UsingApacheIterableMap        avgt   10   125.714 ±   1.935  µs/op
    
  3. Pour une carte avec 100 000 éléments, le score 1184.767 est le meilleur

    Benchmark                          Mode   Cnt  Score        Error    Units
    test1_UsingWhileAndMapEntry        avgt   10   1184.767 ±   332.968  µs/op
    test10_UsingEclipseMap             avgt   10   1191.735 ±   304.273  µs/op
    test2_UsingForEachAndMapEntry      avgt   10   1205.815 ±   366.043  µs/op
    test6_UsingForAndIterator          avgt   10   1206.873 ±   367.272  µs/op
    test8_UsingJava8StreamApiParallel  avgt   10   1485.895 ±   233.143  µs/op
    test5_UsingKeySetAndIterator       avgt   10   1540.281 ±   357.497  µs/op
    test4_UsingKeySetAndForEach        avgt   10   1593.342 ±   294.417  µs/op
    test3_UsingForEachAndJava8         avgt   10   1666.296 ±   126.443  µs/op
    test7_UsingJava8StreamApi          avgt   10   1706.676 ±   436.867  µs/op
    test9_UsingApacheIterableMap       avgt   10   3289.866 ±  1445.564  µs/op
    

Graphes (tests de performance en fonction de la taille de la carte)

Tableau (tests de performance en fonction de la taille de la carte)

          100     600      1100     1600     2100
test10    0.333    1.631    2.752    5.937    8.024
test3     0.309    1.971    4.147    8.147   10.473
test6     0.372    2.190    4.470    8.322   10.531
test1     0.405    2.237    4.616    8.645   10.707
test2     0.376    2.267    4.809    8.403   10.910
test7     0.473    2.448    5.668    9.790   12.125
test9     0.565    2.830    5.952   13.220   16.965
test4     0.808    5.012    8.813   13.939   17.407
test5     0.810    5.104    8.533   14.064   17.422
test8     5.173   12.499   17.351   24.671   30.403

Tous les tests sont sur GitHub .


Pour votre information, vous pouvez également utiliser map.keySet() et map.values() si vous êtes uniquement intéressé par les clés / valeurs de la carte et non par l'autre.


Utilisez Java 8:

map.entrySet().forEach(entry -> System.out.println(entry.getValue()));

Vous pouvez le faire en utilisant des génériques:

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<Integer, Integer> entry = entries.next();
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}

           //Functional Oprations
            Map<String, String> mapString = new HashMap<>();
            mapString.entrySet().stream().map((entry) -> {
                String mapKey = entry.getKey();
                return entry;
            }).forEach((entry) -> {
                String mapValue = entry.getValue();
            });

            //Intrator
            Map<String, String> mapString = new HashMap<>();
            for (Iterator<Map.Entry<String, String>> it = mapString.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();
            }

            //Simple for loop
            Map<String, String> mapString = new HashMap<>();
            for (Map.Entry<String, String> entry : mapString.entrySet()) {
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();

            }

    Iterator iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry element = (Map.Entry)it.next();
        LOGGER.debug("Key: " + element.getKey());
        LOGGER.debug("value: " + element.getValue());    
    }

Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet())
{
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

package com.test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("ram", "ayodhya");
        map.put("krishan", "mathura");
        map.put("shiv", "kailash");

        System.out.println("********* Keys *********");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key);
        }

        System.out.println("********* Values *********");
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        System.out.println("***** Keys and Values (Using for each loop) *****");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out.println("***** Keys and Values (Using while loop) *****");
        Iterator<Entry<String, String>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
                    .next();
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out
                .println("** Keys and Values (Using java 8 using lambdas )***");
        map.forEach((k, v) -> System.out
                .println("Key: " + k + "\t value: " + v));
    }
}






iteration