2010-07-15 5 views
5

J'ai créé une classe Foo qui a la méthode toArray() qui renvoie un Array<Int>.itération sur une HashMap de HashMaps en Java (ou Scala)

Maintenant, j'ai un HashMap mappage des chaînes à HashMaps, qui mappent les objets à Foo. C'est:

HashMap<String,HashMap<Object,Foo>> 

Je veux créer un nouvel objet de type:

HashMap<String,HashMap<Object,Array<Int>>> 

qui est obtenu en appelant la fonction toArray() pour chaque élément Foo dans le hashmap d'origine.

Pour faire normalement je donc faire quelque chose comme:

public static HashMap<String,HashMap<Object,Array<Int>>> changeMap(Map mpOld) { 
     Object key2; 
     String key1; 
     Iterator it2; 
     HashMap<String,HashMap<Object,Array<Int>>> mpNew= 
      new HashMap<String,HashMap<Object,Array<Int>>>() 
     Iterator it1 = mpOld.keySet().iterator(); 
     while (it1.hasNext()) { 
      key1=it1.next(); 
      it2= mpOld.get(key1).keySet().iterator(); 
      mpNew.put(key1,new HashMap<Object,Array<Int>>()) 
      while (it2.hasNext()) { 
       key2=it2.next(); 
       mpNew.get(key1).put(key2,mpOld.get(key1).get(key2).toArray()); 
       //TODO clear entry mpOld.get(key1).get(key2) 
      } 
      //TODO clear entry mpOld.get(key1) 
     } 
     return mpNew; 
    } 

Un code similaire fonctionne très bien, mais la taille de la table de hachage est trop grand pour contenir deux d'entre eux dans la mémoire. Comme vous pouvez le voir, j'ai ajouté deux points où je veux effacer certaines entrées. Le problème est, si je le fais, j'obtiens une erreur de concurrence, ou la boucle d'itérateur se termine juste.

Je me demande s'il existe une meilleure façon de parcourir les cartes et de copier les informations.

Aussi, je travaille dans un projet Scala mais ici je dois utiliser des types Java pour certains problèmes de compatibilité. Bien que Java.util.HashMap ne soit pas un itérateur, peut-être que Scala a une fonction cachée pour gérer cela?

Merci,

Répondre

7

Les itérateurs proposent des méthodes remove(..) qui suppriment en toute sécurité l'élément précédemment consulté. Itérez sur les entrées Key/Value de la carte, convertissez-les et ajoutez-les à la nouvelle carte, et supprimez les anciennes au fur et à mesure.

/** 
* Transfers and converts all entries from <code>map1</code> to 
* <code>map2</code>. Specifically, the {@link Foo} objects of the 
* inner maps will be converted to integer arrays via {@link Foo#toArray}. 
* 
* @param map1 Map to be emptied. 
* @param map2 Receptacle for the converted entries. 
*/ 
private static void transfer(Map<String, Map<Object, Foo>> map1 
     , Map<String, Map<Object, int[]>> map2) { 

    final Iterator<Entry<String, Map<Object, Foo>>> mapIt 
     = map1.entrySet().iterator(); 
    while (mapIt.hasNext()) { 
     final Entry<String, Map<Object, Foo>> mapEntry = mapIt.next(); 
     mapIt.remove(); 
     final Map<Object, int[]> submap = new HashMap<Object,int[]>(); 
     map2.put(mapEntry.getKey(), submap); 
     final Iterator<Entry<Object,Foo>> fooIt 
      = mapEntry.getValue().entrySet().iterator(); 
     while (fooIt.hasNext()) { 
      final Entry<Object,Foo> fooEntry = fooIt.next(); 
      fooIt.remove(); 
      submap.put(fooEntry.getKey(), fooEntry.getValue().toArray()); 
     } 
    } 
} 
4

Je ne l'ai pas eu le temps de le vérifier, mais je pense que quelque chose comme cela devrait fonctionner sur les cartes scala (en supposant que vous utilisez scala 2.8 qui est enfin là):

mpO.mapValues(_.mapValues(_.toArray)) 

Il prendrait votre carte externe, et "remplacer" toutes les cartes internes par une nouvelle, où les valeurs sont les tableaux Int. Les clés et la «structure» générale des cartes restent les mêmes. Selon scaladoc "La carte résultante enveloppe la carte originale sans copier aucun élément.", Donc ce ne sera pas un remplacement réel.

Si vous faites aussi un

import scala.collection.JavaConversions._ 

alors les cartes Java peuvent être utilisés de la même manière que les cartes scala: JavaConversions contiennent un tas de méthodes implicites qui peuvent convertir entre les collections de scala et java.

BTW en utilisant une carte < String, Object HashMap <, Array < Int >>> pourrait ne pas être vraiment pratique à la fin, si je vous j'envisager d'introduire certaines classes qui cachent la complexité de cette construction.

Modifier réflexion à votre commentaire

import scala.collection.JavaConversions._ 
import java.util.Collections._ 

object MapValues { 
    def main(args: Array[String]) { 
    val jMap = singletonMap("a",singletonMap("b", 1)) 
    println(jMap) 
    println(jMap.mapValues(_.mapValues(_+1))) 
    } 
} 

impressions:

{a = {b = 1}}
Carte (a -> Carte (b -> 2))

Montrer que les implicits sont appliqués aussi bien à la carte externe qu'à la carte interne.C'est le but de l'objet JavaConversions: même si vous avez une collection java, vous pouvez l'utiliser comme une classe scala similaire (avec des fonctions boostées).
Vous ne devez pas faire quoi que ce soit d'autre, il suffit d'importer JavaConversions._

+0

Merci, mais bien que j'utilise Scala pour le projet, les HashMaps sont Java HashMaps, de sorte que vous ne pouvez pas appeler mapVAlues sur eux. Est-ce que vous pouvez résoudre ce problème en utilisant JavaConversions? – Skuge

+0

Avez-vous pu résoudre votre problème? Est-ce que mon édition a aidé? –

3

L'ensemble est soutenu par la carte, de sorte que des modifications à la carte se reflètent dans l'ensemble, et vice-versa. Si la carte est modifiée alors qu'une itération sur l'ensemble est en cours (à l'exception de l'opération de suppression de l'itérateur), les résultats de l'itération ne sont pas définis. L'ensemble prend en charge la suppression des éléments, qui supprime le mappage correspondant de la carte, via les opérations Iterator.remove, Set.remove, removeAll, retainAll et clear.

Pourquoi appelez-vous pas la méthode remove() sur la iterator ou set.remove (iterator.next())iterator.next() renvoie la clé, définissez est le keyset et iterator son iterator. PS: essayez également de refactoriser votre structure de données, peut-être quelques classes intermédiaires qui traitent la récupération de données? Une carte dans une carte avec des tableaux comme valeurs ne dit rien et est difficile à suivre.

3

Par exemple en considérant les clés de chaîne; Appelons l'entrée données: Map<String, Map<String, Object>> data

for (Entry<String, Map<String, Tuple>> entry : data.entrySet()) { 
    String itemKey = entry.getKey(); 
    for (Entry<String, Object> innerEntry : entry.getValue().entrySet()) { 
    String innerKey = innerEntry.getKey(); 
    Object o = innerEntry.getValue(); 
    // whatever, here you have itemKey, innerKey and o 
    } 
} 
Questions connexes