I have the following data structure:
java.util.Map[List[String],List[String]] = {[10, 20]=[1500], [5, 7]=[1400]}
I am trying to extract the numbers 10 20 5 and 7 using Scala. The way I was looking to achieve this is:
map.head._1 -> to extract 10 (map.head returns a tuple)
map.head._2 -> to extract 20 (map.head returns a tuple)
However, I am getting the following exception:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to scala.collection.immutable.List
I have read about importing import scala.collection.JavaConversions._ however, this did not fix anything.
Thanks, any help is highly appreciated!
The piece of code that tries to achieve this is:
def getTokenRangeForKeys(params: String): java.util.Map[List[String], List[String]] = {
invokeOperation[java.util.Map[List[String], List[String]]]("XXX", "YYY", Array(params))
}
The above method returns my map, which looks like this:
map = java.util.Map[List[String],List[String]] = {[10, 20]=[1500], [5, 7]=[1400]}
What I have tried so far:
map.head._1 -> java.lang.ClassCastException: java.util.ArrayList cannot be cast to scala.collection.immutable.List
scalaMap = map.asScala
m.headOption match {
case Some((h1, h2)) => println((h1, h2)) -> java.util.ArrayList cannot be cast to scala.collection.immutable.List
case None => ...
}
I think your declaration of what comes from Java world should be:
java.util.Map[java.util.List[String], java.util.List[String]]
In the current form of java.util.Map[List[String], List[String]] you declare a java Map of Scala Lists, which is probably not what you want. JVM is not complaining when you pass your Java types because only top level type is checked as a part of function signature check - this is called type erasure.
On this you should use JavaConverters asScala to convert to corresponding Scala types as written in the Reactormonk answer:
import scala.collection.JavaConverters._
val m = map.asScala.map{case (h, k) => (h.asScala, k.asScala)}
Don't use JavaConversions.
import scala.collection.JavaConverters._
val m = map.asScala.map({case (h, k) => (h.asScala, k.asScala)})
m.headOption match {
case Some((h1, h2)) => ...
case None => ...
}
Related
my Task is to read registrations from a file given like:
Keri,345246,2
Ingar,488058,2
Almeta,422016,1
and insert them into a list(Tuple of (String, Int, Int).
So far I wrote this:
The problem is that I don‘t understand why I can't try to cast value2 and value3 to Int even tho they should be Strings because they come from an Array of Strings. Could someone tell me, what my mistake is, I am relatively new to Scala
What is the point of using Scala if you are going to write Java code?
This is how you would properly read a file as a List of case classes.
import scala.io.Source
import scala.util.Using
// Use proper names for the fields.
final case class Registration(field1: String, field2: Int, field3: Int)
// You may change the error handling logic.
def readRegistrationsFromFile(fileName: String): List[Registration] =
Using(Source.fromFile(fileName)) { source =>
source.getLines().map(line => line.split(',').toList).flatMap {
case field1Raw :: field2Raw :: field3Raw :: Nil =>
for {
field2 <- field2Raw.toIntOption
field3 <- field3Raw.toIntOption
} yield Registration(field1 = field1Raw.trim, field2, field3)
case _ =>
None
}.toList
}.getOrElse(default = List.empty)
(feel free to ask any question you may have about this code)
In Scala, in order to convert a String to an Int you need explicit casting.
This can be achieved like this if you are sure the string can be parsed into a integer:
val values = values(1).toInt
If you cannot trust the input (and you probably should not), you can use .toIntOption which will give you a Option[Int] defined if the value was converted successfully or undefined if the string did not represent an integer.
The previous answers are correct. I would add a few more points.
saveContent is declared as a val. This is means it cannot be changed (assigned another value). You can use the Scala REPL (command-line) tool to check:
scala> val saveContent = Nil
val v: collection.immutable.Nil.type = List()
scala> saveContent = 3
^
error: reassignment to val
Instead, you could use a var, although it would be more idiomatic to have an overall pattern like the one provided by Luis Miguel's answer - with pattern-matching and a for-comprehension.
You can use the Scala REPL to check the types of the variables, too. Splitting a String will always lead to more Strings, not Ints, etc.
> val values = "a,2,3".split(",")
val values: Array[String] = Array(a, 2, 3)
> values(2)
val res3: String = 3
This is why a cast like Gael's is necessary.
Array-type access is done with parentheses and not square brackets, in Scala. See above, and http://scalatutorials.com/tour/interactive_tour_of_scala_lists for more details.
I have a sequence of Errors or Views (Seq[Xor[Error,View]])
I want to map this to an Xor of the first error (if any) or a Sequence of Views
(Xor[Error, Seq[View]]) or possibly simply (Xor[Seq[Error],Seq[View])
How can I do this?
You can use sequenceU provided by the bitraverse syntax, similar to as you would do with scalaz. It doesn't seem like the proper type classes exist for Seq though, but you can use List.
import cats._, data._, implicits._, syntax.bitraverse._
case class Error(msg: String)
case class View(content: String)
val errors: List[Xor[Error, View]] = List(
Xor.Right(View("abc")), Xor.Left(Error("error!")),
Xor.Right(View("xyz"))
)
val successes: List[Xor[Error, View]] = List(
Xor.Right(View("abc")),
Xor.Right(View("xyz"))
)
scala> errors.sequenceU
res1: cats.data.Xor[Error,List[View]] = Left(Error(error!))
scala> successes.sequenceU
res2: cats.data.Xor[Error,List[View]] = Right(List(View(abc), View(xyz)))
In the most recent version of Cats Xor is removed and now the standard Scala Either data type is used.
Michael Zajac showed correctly that you can use sequence or sequenceU (which is actually defined on Traverse not Bitraverse) to get an Either[Error, List[View]].
import cats.implicits._
val xs: List[Either[Error, View]] = ???
val errorOrViews: Either[Error, List[View]] = xs.sequenceU
You might want to look at traverse (which is like a map and a sequence), which you can use most of the time instead of sequence.
If you want all failing errors, you cannot use Either, but you can use Validated (or ValidatedNel, which is just a type alias for Validated[NonEmptyList[A], B].
import cats.data.{NonEmptyList, ValidatedNel}
val errorsOrViews: ValidatedNel[Error, List[View]] = xs.traverseU(_.toValidatedNel)
val errorsOrViews2: Either[NonEmptyList[Error], List[View]] = errorsOrViews.toEither
You could also get the errors and the views by using MonadCombine.separate :
val errorsAndViews: (List[Error], List[View]) = xs.separate
You can find more examples and information on Either and Validated on the Cats website.
I am thoroughly confused by this behavior in the Scala REPL:
scala> import java.util.Map
import java.util.Map
scala> import java.util.HashMap
import java.util.HashMap
scala> val jMap:java.util.Map[String,Int]=new HashMap[String,Int]("first"->1,"second" -> 2)
<console>:12: error: type mismatch;
found : (String, Int)
required: Float
val jMap =new HashMap[String,Int]("first"->1,"second" -> 2)
^
<console>:12: error: type mismatch;
found : (String, Int)
required: Float
val jMap=new HashMap[String,Int]("first"->1,"second" -> 2)
^
Can someone help explain what's going on here ?
java.util.HashMap does not provide capability to construct the map by passing a vararg of (K, V), but there is a two-arg constructor accepting initialCapacity: Int and loadFactor: Float, that's why you're getting the compile error about a Float being required.
(updated for scala 2.13+) The idiomatic approach in Scala would be to just use Scala immutable maps (no imports required):
val map = Map("first" -> 1, "second" -> 2).asJava
If your Scala code works with a Java library that returns a java.util.Map, you can convert it into a Scala map explicitly by using scala.jdk.CollectionConverters like so:
import scala.jdk.CollectionConverters._
val javaMap: java.util.Map[String, String] = // ...
val map = javaMap.asScala
// or vice versa if you need to pass it into the Java API
val javaMap = Map("first" -> 1, "second" -> 2).asJava
Note that asScala is just a wrapper to the underlying Java map (so if that one is mutable, the wrapped map will also be mutable) and is an instance of scala.collection.Map. In order to be fully idiomatic and benefit from Scala's immutability guarantees, you might need to add another .toMap at the end that will convert it to scala.collection.immutable.Map (which is the default Map).
If you use scala 2.12 or older, instead of scala.jdk.CollectionConverters._ import scala.collection.JavaConverters._.
Converting from java.util.ArrayList to scala.collection.immutable.List, the 2.10 compiler and run-time may seem to be in disagreement, about the type of val emits:
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
//return(emits)
return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields:
converted from class java.util.ArrayList to class scala.collection.immutable.$colon$colon
Now changing to return the real value by changing only the return line -
import org.ahocorasick.trie._
import scala.collection.JavaConverters._ // convert Java colllections to Scala ones
object wierd {
val trie = new Trie
def trieInit(patterns: List[String]) {
trie.onlyWholeWords();
for (pattern <- patterns)
trie.addKeyword(pattern)
}
def patternTest(text : String) : List[String] =
{
val emitsJ = trie.parseText(text)
val emits = emitsJ.asScala map (i => i.getKeyword)
println(s"converted from ${emitsJ.getClass} to ${emits.getClass}")
return(emits)
//return (List.empty[String])
}
trieInit(List("hello"))
patternTest("hello")
}
Yields a compilation error:
[error] reproduce.scala:23: type mismatch;
[error] found : Iterable[String]
[error] required: List[String]
[error] return(emits)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
What would be the simple explanation for this?
How would I better approach the conversion?
JavaConverters convert to the Scala collection closest to the Java collection that it has pimped. You still have to call toList to further transform it to the collection you want:
val emits = emitsJ.asScala.toList map (i => i.getKeyword)
See related: What is the difference between JavaConverters and JavaConversions in Scala?
So what's happening here is that the underlying object returned to you from asScala is a List but it's been downcast to an Iterable since List <: Iterable this is all fine until you want to use the Iterable as a list. The easiest option is to call toList on it.
For a little more detail you can browse the source:
case class JCollectionWrapper[A](underlying: ju.Collection[A]) extends AbstractIterable[A] with Iterable[A] {
def iterator = underlying.iterator
override def size = underlying.size
override def isEmpty = underlying.isEmpty
def newBuilder[B] = new mutable.ArrayBuffer[B]
}
So your call to asScala gives you back this wrapper. Next, your call to map is using this CanBuildFrom which is then used in this map operation, which finally gives you the result which happens to be a List because the builder is a ListBuffer and the result is a List, which again happens to be an Iterable because the can build from pattern downcasts to Iterable. Hope that explains everything :)
the return type of trie.parseText is declared as java.util.Collection[Emit]. This isn't very specific, there are lots of possible subtypes of Collection, they aren't specifying what specific type they are going to return, it could be a TreeSet, it could be a Vector, it could be an ArrayList. But as far as the compiler is concerned, it could be anything which is a sub-type of Collection. You have inspected it at run-time and seen that for some particular input, it happened to return an ArrayList, but there is no way the compiler could know this.
When you call .asScala on this, you are using this implicit definition from JavaConverters
implicit def iterableAsScalaIterableConverter[A](i: java.lang.Iterable[A]): convert.Decorators.AsScala[Iterable[A]]
Which converts a java.lang.Itaerable into a scala.collection.Iterable.
There is no converter for Collection so you get a converter for the next most specific thing, Iterable. you call map on this Iterable and get an Iterable back.
Now, just like you have inspected the runtime value of the Collection returned from parseText and seen that it was a ArrayList you have inspected the value returned from this map operation and have seen that it is a scala.collection.immutable.List, but again, the compiler can't know this, all that it can know is that you gotten something which is a subclass of Iterable back.
Simply calling .toList on the resulting Iterable should be all you need to do.
I have java API which return this type:
ArrayList[ArrayList[String]] = Foo.someJavaMethod()
In scala program, I need to send above type as a parameter to a scala function 'bar' whose type is
def bar(param: List[List[String]]) : List[String] = {
}
so I call bar like:
val list = bar(Foo.someJavaMethod())
but this does not work as I get compile error.
I thought have this import
import scala.collection.JavaConversions._
will do implicit automatic conversion between Java and Scala collections.
I also tried using like:
Foo.someJavaMethod().toList
but that does not work either.
What is the solution to this problem?
First, ArrayList does not convert to List, it converts to a Scala Buffer. Second, implicit conversion will not recurse into the elements of your collections.
You'll have to manually map the inner lists. Either with implicit conversions:
import collection.JavaConversions._
val f = Foo.someJavaMethod()
bar(f.toList.map(_.toList))
Or, more explicitly, if you prefer:
import collection.JavaConverters._
val f = Foo.someJavaMethod()
bar(f.asScala.toList.map(_.asScala.toList))