2013-06-11 21 views
5

Io lavoro su un programma Scala che chiama una funzione da una libreria Java, elabora i risultati e sputa un CSV.Come posso convertire una mappa Java di mappe per l'uso in Scala?

La funzione Java in questione si presenta così:

Map<String, Map<String, AtomicLong>> getData(); 

La Scala:

import scala.collection.JavaConversions._ 
    def analysisAndCsvStuff(data: Map[String, Map[String, AtomicLong]]): Unit { ... } 

L'errore:

type mismatch; 
    found:java.util.Map[java...String,java...Map[java...String,java...AtomicLong]] 
    required: scala...Map[String,scala...Map[String,java...AtomicLong]] 

(I nomi di percorso stavano rovinando la formattazione.)

Immagino che JavaConversions possa convertire correttamente la java esterna ... Mappa ma non la java interna ... Mappa. Ho visto this question ma non sono sicuro di come scrivere una "conversione implicita esplicita".

risposta

8

Edit: il recommended way è quello di utilizzare JavaConverters e il metodo .asScala:

import scala.collection.JavaConverters._ 
val myScalaMap = myJavaMap.asScala.mapValues(_.asScala) 

Nota che si otterrà mappe mutabili fuori da questo. Puoi sempre usare .asScala.toMap se vuoi quelli immutabili.


La risposta originale con JavaConversions:

La risposta breve è: chiamare .mapValues sulla mappa esterna per convertire la cartina interno:

import scala.collection.JavaConversions._ 
val myScalaMap = myJavaMap.mapValues(_.toMap) 

.mapValues costringe la conversione o l'esterno mappa a scala Map e .toMap obbliga la conversione della mappa interna a una scala (immutabile) mappa. La parte immutabile non è strettamente necessaria, ma comunque ...

Questo è molto simile a this anwser. Breve esempio:

scala> val a: java.util.Map[String, java.util.Map[String, String]] = new java.util.HashMap[String, java.util.Map[String, String]] 
a: java.util.Map[String,java.util.Map[String,String]] = {} 


scala> import scala.collection.JavaConversions._ 
import scala.collection.JavaConversions._ 

scala> val myScalaMap = a.mapValues(_.toMap) 
myScalaMap: scala.collection.Map[String,scala.collection.immutable.Map[String,String]] = Map() 
+0

Huzzah! Funziona perfettamente. Grazie per la risposta rapida e concisa! –

+0

@SamDaniel np :). Vedi il mio aggiornamento, anche se entrambe le versioni sono perfettamente valide. – gourlaysama

+1

Penso che la versione di JavaConverters. Ho commentato la risposta collegata che la gente consiglia vivamente di utilizzare asScala. –

0

Prima di tutto, ci sono 2 versioni di Maps e altre importanti collezioni in Scala, mutevole ed immutabili. L'impostazione predefinita è la versione immutabile ed è possibile accedere alla versione mutabile tramite il pacchetto scala.collection.mutable.

A causa della mutabilità di java Collections, la conversione di collezioni java in collezioni scalabili è molto più semplice rispetto alla conversione in scale immutabili. JavaConversions non è in grado di convertire le raccolte java in collezioni scala immutabili.

Inoltre, JavaCollections non è in grado di convertire le raccolte interne in tipi scala.

Ti suggerisco di convertire la tua java Map come una struttura di dati scala come segue.

val javaMap:java.util.HashMap[String,java.util.Map[String,Int]] = new java.util.HashMap() 

val scalaMap:Map[String,Map[String,Int]] = javaMap.toMap.map { case (k,v) => k -> v.toMap} 

Procedimento .tomap è definita scala.collection.mutable.Map che converte mutable Map a immutable Map. e JavaConversions converte java.util.Map a Scala immutabile Map

2

I am unsure of how to go about writing an "explicit implicit conversion"

Utilizzando JavaConverters naturalmente, quella frase suggerisce una implicita personalizzato.

Ecco l'avanti e indietro:

scala> import scala.collection.JavaConverters._ 
import scala.collection.JavaConverters._ 

scala> import java.util.concurrent.atomic.AtomicLong 
import java.util.concurrent.atomic.AtomicLong 

scala> val m = Map("a" -> Map("b" -> new AtomicLong(7L))) 
m: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,java.util.concurrent.atomic.AtomicLong]] = Map(a -> Map(b -> 7)) 

scala> val j = m mapValues (_.asJava) asJava 
warning: there were 1 feature warning(s); re-run with -feature for details 
j: java.util.Map[String,java.util.Map[String,java.util.concurrent.atomic.AtomicLong]] = {a={b=7}} 

scala> implicit class mapmap[A,B,C](val v: 
    | java.util.Map[A,java.util.Map[B,C]]) extends AnyVal { 
    | def asMapMap: Map[A,Map[B,C]] = 
    | v.asScala.toMap.mapValues(_.asScala.toMap) 
    | } 
defined class mapmap 

scala> j.asMapMap 
res0: Map[String,Map[String,java.util.concurrent.atomic.AtomicLong]] = Map(a -> Map(b -> 7)) 
+0

Grazie! L'implicito di Scala mi è sembrato un voodoo all'inizio, un n00b di Scala e qualcuno che si è allenato nelle arti del sistema di tipo ML. Ma sono abbastanza sicuro di capire come funziona ora. –

0

alternativa è possibile utilizzare scalaj-collection biblioteca ho scritto appositamente per questo scopo

import com.daodecode.scalaj.collection._ 

analysisAndCsvStuff(getData.deepAsScala) 

il gioco è fatto. Converte tutte le raccolte java nidificate e i tipi primitivi in ​​versioni scala. È inoltre possibile convertire direttamente in strutture di dati immutabili utilizzando deepAsScalaImmutable (con alcuni overhead di copia ovviamente)

Problemi correlati