2010-07-15 21 views
5

Creé una clase Foo que tiene el método toArray() que devuelve Array<Int>.Iteración sobre un HashMap de HashMaps en Java (o Scala)

Ahora, tengo una asignación de HashMap Cadenas a HashMaps, que asignan objetos a Foo. Es decir:

HashMap<String,HashMap<Object,Foo>> 

y quiero crear un nuevo objeto de tipo:

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

que se obtiene llamando a la función toArray() para cada elemento de Foo en el Hashmap originales.

para hacerlo que normalmente haría algo como:

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 código similar funciona bien, pero el tamaño de la HashMap es demasiado grande como para contener dos de ellos en la memoria. Como puede ver, agregué dos puntos en los que quiero borrar algunas entradas. El problema es que, si lo hago, obtengo un error de concurrencia o el ciclo del iterador simplemente termina.

Me pregunto si existe una forma mejor de recorrer los mapas y copiar la información.

Además, estoy trabajando en un proyecto de Scala pero aquí tengo que usar los tipos de Java para algunos problemas de compatibilidad. Aunque Java.util.HashMap no es un iterador, ¿tal vez Scala tiene alguna funcionalidad oculta para lidiar con esto?

Gracias,

Respuesta

7

Iteradores ofrecen remove(..) métodos que eliminan de forma segura el elemento al que se accedió anteriormente. Itere sobre las entradas clave/valor del mapa, convirtiéndolos y agregándolos al nuevo mapa, y eliminando los antiguos sobre la marcha.

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

yo no tenía tiempo para comprobarlo, pero supongo que algo como esto debería funcionar en Mapas Scala (suponiendo que el uso Scala 2.8, que es finalmente aquí):

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

Se tomaría su mapa exterior, y "reemplazará" todos los mapas interiores por uno nuevo, donde los valores son los arrays Int. Las claves y la "estructura" general de los mapas siguen siendo las mismas. De acuerdo con scaladoc "El mapa resultante envuelve el mapa original sin copiar ningún elemento.", Por lo que no será un reemplazo real.

Si también hace un

import scala.collection.JavaConversions._ 

continuación, los mapas de Java se pueden utilizar del mismo modo que los mapas Scala: JavaConversions contienen un montón de métodos implícitos que pueden convertir entre las colecciones Scala y java.

BTW mediante una cadena de Mapa <, HashMap < objeto, conjunto < Int >>> podría no ser muy conveniente al final, si yo fuera usted quisiera considerar la introducción de algunas clases que ocultar la complejidad de esta construcción.

Editar refleja a su comentario

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

impresiones:

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

Mostrando que las implicidades se aplican tanto al mapa exterior como al interno bastante bien.Este es el propósito del objeto JavaConversions: incluso si tiene una colección java, puede usarla como una clase scala similar (con características mejoradas).
Usted no tiene que hacer nada más, sólo importar JavaConversions._

+0

Gracias, pero a pesar de que estoy usando Scala para el proyecto, los HashMaps son HashMaps Java, por lo que no se puede llamar mapVAlues en ellos. ¿Hay alguna manera de resolver esto usando JavaConversions? – Skuge

+0

¿Pudo resolver su problema? ¿Mi edición ayudó? –

3

El conjunto está respaldado por el mapa, por lo que los cambios en el mapa se reflejan en el conjunto, y viceversa. Si el mapa se modifica mientras una iteración sobre el conjunto está en progreso (excepto a través de la propia operación de eliminación del iterador), los resultados de la iteración no están definidos. El conjunto admite la eliminación de elementos, que elimina la asignación correspondiente del mapa, a través de Iterator.remove, Set.remove, removeAll, retainAll y borra las operaciones.

¿Por qué no se llama al método remove() en el iterador o set.remove (iterator.next()) donde iterator.next() devuelve la clave, establecer es el conjunto de claves y su iterador iterador.

PD: también intenten refactorizar su estructura de datos, ¿tal vez algunas clases intermedias que manejan la recuperación de datos? Un mapa en un mapa con matrices como valores no dice nada y es difícil seguirlo.

3

Por ejemplo, teniendo en cuenta las claves de cadena; Deja llamada la entrada de datos : 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 
    } 
}