java.util.Iterator to Scala list?

22,093

Solution 1

That would work if obj.getKeys() was a java.util.Iterator<String>. I suppose it is not.

If obj.getKeys() is just java.util.Iterator in raw form, not java.util.Iterator<String>, not even java.util.Iterator<?>, this is something scala tend to dislikes, but anyway, there is no way scala will type your expression as List[String] if it has no guarantee obj.getKeys() contains String.

If you know your iterator is on Strings, but the type does not say so, you may cast :

obj.getKeys().asInstanceOf[java.util.Iterator[String]]

(then go on with .asScala.toList)

Note that, just as in java and because of type erasure, that cast will not be checked (you will get a warning). If you want to check immediately that you have Strings, you may rather do

obj.getKeys().map(_.asInstanceOf[String])

which will check the type of each element while you iterate to build the list

Solution 2

You don't need to call asScala, it is an implicit conversion:

import scala.collection.JavaConversions._

val javaList = new java.util.LinkedList[String]() // as an example

val scalaList = javaList.iterator.toList

If you really don't have the type parameter of the iterator, just cast it to the correct type:

javaList.iterator.asInstanceOf[java.util.Iterator[String]].toList

EDIT: Some people prefer not to use the implicit conversions in JavaConversions, but use the asScala/asJava decorators in JavaConverters to make the conversions more explicit.

Solution 3

I dislike the other answers. Hell, I dislike anything that suggests using asInstanceOf unless there's no alternative. In this case, there is. If you do this:

private lazy val keys : List[String] = obj.getKeys().asScala.collect { 
    case s: String => s 
}.toList

You turn the Iterator[_] into a Iterator[String] safely and efficiently.

Solution 4

Note that starting Scala 2.13, package scala.jdk.CollectionConverters replaces deprecated packages scala.collection.JavaConverters/JavaConversions when it comes to implicit conversions between Java and Scala collections:

import scala.jdk.CollectionConverters._

// val javaIterator: java.util.Iterator[String] = java.util.Arrays.asList("a", "b").iterator
javaIterator.asScala
// Iterator[String] = <iterator>
Share:
22,093
Martin Smith
Author by

Martin Smith

Updated on June 09, 2020

Comments

  • Martin Smith
    Martin Smith almost 4 years

    I have the following code:

    private lazy val keys: List[String] = obj.getKeys().asScala.toList
    

    obj.getKeys returns a java.util.Iterator<java.lang.String>

    Calling asScala, via JavaConverers (which is imported) according to the docs..

    java.util.Iterator <==> scala.collection.Iterator 
    

    scala.collection.Iterator defines

    def toList: List[A] 
    

    So based on this I believed this should work, however here is the compilation error:

    [scalac]  <file>.scala:11: error: type mismatch;
    [scalac]  found   : List[?0] where type ?0
    [scalac]  required: List[String]
    [scalac]  private lazy val keys : List[String] = obj.getKeys().asScala.toList
    [scalac]  one error found
    

    I understand the type parameter or the java Iterator is a Java String, and that I am trying to create a list of Scala strings, but (perhaps naively) thought that there would be an implicit conversion.

  • Martin Smith
    Martin Smith over 12 years
    You are right, there is no type parameter for the Iterator that is being returned from the getKeys() method. Very astute observation, thank you. Though it appears I do need the asScala in there, in that case. Perhaps there is something else I am missing.
  • Martin Smith
    Martin Smith over 12 years
    Thanks very much for the answer! seems like you and Matthew Farwell agree for the most part. I did what you suggest, obj.getKeys().asInstanceOf[java.util.Iterator[String]].asSca‌​la.toList and that seems to work, and I did not get a compilation warning.
  • Daniel C. Sobral
    Daniel C. Sobral over 12 years
    The implicit conversion is on JavaConversions, but not on JavaConverters. That uses asScala. Many people, myself included, prefer to make this conversion explicit to avoid subtle bugs.
  • Matthew Farwell
    Matthew Farwell over 12 years
    @Daniel I guess making stuff explicit is a good idea, thanks for the tip.