2010-07-15 12 views
5

Ich erstellte eine Klasse Foo, die die Methode toArray() hat, die eine Array<Int> zurückgibt.Iterieren über eine HashMap von HashMaps in Java (oder Scala)

Jetzt habe ich eine HashMap Zuordnung Strings zu HashMaps, die Objekte zu Foo zuordnen. Das heißt:

HashMap<String,HashMap<Object,Foo>> 

Und ich möchte ein neues Objekt des Typs schaffen:

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

, die durch den Aufruf der Funktion toArray erhalten() für jedes Element Foo in der ursprünglichen Hashmap.

Um dies zu tun ich normalerweise etwas wie tun würde:

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; 
    } 

Ein ähnlicher Code funktioniert gut, aber die Größe des HashMap ist zu groß, zwei von ihnen in Erinnerung zu halten. Wie Sie sehen können, habe ich zwei Punkte hinzugefügt, wo ich einige Einträge löschen möchte. Das Problem ist, wenn ich es tue, bekomme ich entweder einen Gleichzeitigkeitsfehler, oder die Iteratorschleife beendet gerade.

Ich frage mich, ob es eine bessere Möglichkeit gibt, durch die Maps zu iterieren und die Informationen zu kopieren.

Auch arbeite ich in einem Scala-Projekt, aber hier muss ich Java-Typen für einige Kompatibilitätsprobleme verwenden. Obwohl Java.util.HashMap kein Iterator ist, hat Scala vielleicht eine versteckte Funktionalität, um damit umzugehen?

Danke,

Antwort

7

Iteratoren bieten remove(..) Methoden an, die den zuvor aufgerufenen Artikel sicher entfernen. Iterieren Sie über die Key/Value-Einträge der Map, konvertieren Sie sie und fügen Sie sie zur neuen Map hinzu, und entfernen Sie die alten, während Sie fortfahren.

/** 
* 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

ich keine Zeit, es zu prüfen hatte, aber ich denke, so etwas wie dies auf scala Karten funktionieren sollte (vorausgesetzt, Sie verwenden scala 2.8, die schließlich hier ist):

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

Es würde Ihre äußere Karte nehmen und alle inneren Karten durch eine neue ersetzen, wobei die Werte die Int-Arrays sind. Schlüssel und die allgemeine "Struktur" der Karten bleiben gleich. Nach scaladoc "Die resultierende Karte umschließt die ursprüngliche Karte, ohne irgendwelche Elemente zu kopieren.", So wird es kein echter Ersatz sein.

Wenn Sie auch eine

import scala.collection.JavaConversions._ 

tun dann die Java-Karten können auf die gleiche Weise wie scala Karten verwendet werden: JavaConversions enthalten eine Reihe von impliziten Methoden, die zwischen scala und Java-Sammlungen umwandeln kann.

BTW eine Karte < String verwenden, HashMap < Objekt, Array < Int >>> vielleicht nicht wirklich bequem sein, am Ende, wenn ich Sie wäre, würde ich erwägen einige Klassen einzuführen, die die Komplexität dieses Konstrukts verstecken würde.

Bearbeiten, um Ihren Kommentar

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))) 
    } 
} 

Drucke reflektieren:

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

Zeigen, dass die implicits sowohl auf die äußere als auch auf die innere Karte ganz schön angewendet werden.Dies ist der Zweck des JavaConversions-Objekts: Selbst wenn Sie eine Java-Sammlung haben, können Sie sie als eine ähnliche Scala-Klasse verwenden (mit verstärkten Funktionen).
Sie müssen sonst nichts tun, importieren nur JavaConversions._

+0

Danke, aber obwohl ich Scala für das Projekt bin verwenden, sind die HashMaps Java HashMaps, so dass Sie nicht mapVAlues auf sie anrufen können. Ist das eine Möglichkeit, dies mit JavaConversions zu lösen? – Skuge

+0

Konnten Sie Ihr Problem lösen? Hat meine Hilfe bearbeitet? –

3

Der Satz von der Karte unterstützt wird, so dass Änderungen an der Karte in der Reihe reflektiert werden, und umgekehrt. Wenn die Zuordnung geändert wird, während eine Iteration über die Menge ausgeführt wird (außer durch die eigene Entfernungsoperation des Iterators), sind die Ergebnisse der Iteration nicht definiert. Der Satz unterstützt das Entfernen von Elementen, wodurch das entsprechende Mapping über die Operationen Iterator.remove, Set.remove, removeAll, retainAll und clear gelöscht wird.

Warum Sie die remove() Methode auf dem Iterator nicht anrufen oder set.remove (iterator.next()) wo iterator.next() den Schlüssel zurückgibt, legen die Keyset und Iterator seine Iterator.

PS: versuchen Sie auch, Ihre Datenstruktur zu refaktorieren, vielleicht einige Zwischenklassen, die den Datenabruf handhaben? Eine Karte in einer Karte mit Arrays als Werten sagt nichts aus und ist schwer zu verfolgen.

3

Zum Beispiel String-Schlüssel; die Eingabe nennen wir Daten: 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 
    } 
} 
Verwandte Themen