2013-06-11 16 views
5

Pracuję nad programem Scala, który wywołuje funkcję z biblioteki Java, przetwarza wyniki i wypisuje plik CSV.Jak mogę przekonwertować mapę map Java do użycia w Scali?

Funkcja Java na pytanie wygląda następująco:

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

SCALA:

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

Błąd:

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

(Nazwy ścieżek zostały rujnuje formatowanie).

Zgaduję, że JavaConversions może pomyślnie konwertować zewnętrzną mapę Java ... Map, ale nie wewnętrznej java ... Map. Widziałem this question, ale nie jestem pewien, jak napisać "wyraźną niejawną konwersję".

Odpowiedz

8

Edit: the recommended way jest użycie JavaConverters i metoda .asScala:

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

Zauważ, że dostaniesz zmienne mapy z tego. Zawsze możesz użyć .asScala.toMap, jeśli chcesz niezmienne.


Oryginalny odpowiedź z JavaConversions:

Odpowiedź jest krótka: zadzwoń .mapValues na zewnętrznej mapie przekonwertować mapę wewnętrzna:

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

.mapValues wymusza konwersję lub zewnętrznej mapowanie do scala Map i .toMap wymusza zamianę wewnętrznej mapy na scala (niezmienna) mapę. Niezmienna część nie jest bezwzględnie konieczna, ale tak czy inaczej ...

Jest to bardzo podobne do this anwser. Krótki przykład:

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! Działa świetnie. Dzięki za szybką i zwięzłą odpowiedź! –

+0

@SamDaniel np :). Zobacz moją aktualizację, mimo że obie wersje są całkowicie poprawne. – gourlaysama

+1

Myślę, że wersja JavaConverters. Skomentowałem połączoną odpowiedź, że ludzie zdecydowanie zalecają używanie asScala. –

0

Przede wszystkim, są 2 wersje Maps i inne większe zbiory w Scala, Zmienne i niezmienne. Domyślnym zastosowaniem jest wersja niezmienna, a dostęp do wersji zmiennej możliwy jest poprzez pakiet scala.collection.mutable.

Z powodu zmienności kolekcji java konwersja kolekcji java na zmienne zbiory scala jest znacznie łatwiejsza niż konwersja do niezmiennych kolekcji scala. JavaConversions nie może konwertować kolekcji java na niezmienne kolekcje scala.

Ponadto JavaCollections nie może konwertować twoich wewnętrznych kolekcji na typy scala.

Proponuję przekonwertować swoją mapę Java jako strukturę danych scala w następujący sposób.

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} 

Sposób .tomap jest zdefiniowana w scala.collection.mutable.Map który przekształca mutable Map do immutable Map. i JavaConversions konwertuje java.util.Map do Scala niezmiennej Map

2

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

Korzystanie JavaConverters oczywiście, że wyrażenie sugeruje zwyczaj niejawny.

Oto tam iz powrotem:

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

Dzięki! Scala "implicit" wydawała mi się początkowo voodoo, Scala n00b i ktoś wyszkolony w sztuce systemu typu ML. Ale jestem pewien, że rozumiem, jak to działa teraz. –

0

alternatywnie można użyć scalaj-collection bibliotekę napisałem specjalnie do tego celu

import com.daodecode.scalaj.collection._ 

analysisAndCsvStuff(getData.deepAsScala) 

to wszystko. Konwertuje wszystkie zagnieżdżone kolekcje java i typy pierwotne na wersje scala. Możesz także przekonwertować bezpośrednio na niezmienne struktury danych przy użyciu deepAsScalaImmutable (z pewnym nakładem kosztów związanych z kopiowaniem).

Powiązane problemy