如何将java.util.Iterator转换为Scala列表?

23

我有以下代码:

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

obj.getKeys 返回一个 java.util.Iterator<java.lang.String>

根据文档,通过导入的 JavaConverers 调用 asScala 方法。

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

scala.collection.Iterator 定义了

def toList: List[A] 

所以基于这个,我认为应该可以工作,但是这里出现了编译错误:

[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

我知道 Java Iterator 的类型参数是 Java String,而我正在尝试创建一个 Scala 字符串列表,但是(也许天真地)认为会有隐式转换。

5个回答

27

您不需要调用asScala,它是一个隐式转换:

import scala.collection.JavaConversions._

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

val scalaList = javaList.iterator.toList

如果你真的没有迭代器的类型参数,只需将其强制转换为正确的类型:

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

编辑:一些人更喜欢不使用JavaConversions中的隐式转换,而是使用JavaConverters中的asScala/asJava修饰符使转换更加显式。


你说得对,从getKeys()方法返回的迭代器中并没有类型参数。非常敏锐的观察,谢谢。虽然看起来我确实需要asScala,但是也许还有其他我忽略了的东西。 - rshepherd
1
隐式转换在JavaConversions上,但不在JavaConverters上。它使用asScala。许多人,包括我自己,更喜欢将此转换明确化以避免微妙的错误。 - Daniel C. Sobral
@Daniel 我想明确表达是个好主意,谢谢你的建议。 - Matthew Farwell

11

如果 obj.getKeys() 是一个 java.util.Iterator<String>,那么这将起作用。我想它不是。

如果 obj.getKeys() 只是原始形式的 java.util.Iterator,而不是 java.util.Iterator<String>,甚至不是 java.util.Iterator<?>,这是一些scala不喜欢的东西,但无论如何,如果没有保证 obj.getKeys() 包含字符串,scala 没有办法将您的表达式类型化为 List[String]

如果您知道您的迭代器是基于字符串的,但类型没有说明,您可以进行转换:

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

(然后使用.asScala.toList进行下一步操作)

请注意,与Java一样,由于类型擦除,该强制转换将不会被检查(您将收到警告)。如果您想立即检查是否为字符串,则可以选择执行以下操作

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

在迭代过程中检查每个元素的类型以构建列表


1
非常感谢您的回答!看起来您和Matthew Farwell在大部分方面都是一致的。我按照您的建议进行了操作,obj.getKeys().asInstanceOf[java.util.Iterator[String]].asScala.toList,这似乎有效,并且我没有收到编译警告。 - rshepherd

3

我不喜欢其他答案。其实,除非别无选择,否则我不建议使用asInstanceOf。在这种情况下,有一个更好的替代方案。如果你这样做:

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

您可以安全高效地将Iterator[_]转化为Iterator[String]

2
请注意,从Scala 2.13开始,包scala.jdk.CollectionConverters取代了已弃用的包scala.collection.JavaConverters/JavaConversions,用于Java和Scala集合之间的隐式转换。"Original Answer"翻译成"最初的回答"。
import scala.jdk.CollectionConverters._

// val javaIterator: java.util.Iterator[String] = java.util.Arrays.asList("a", "b").iterator
javaIterator.asScala
// Iterator[String] = <iterator>

0

从 Scala 2.12.8 开始,可以使用

import scala.collection.JavaConverters._

asScalaIterator(java.util.Iterator variable).toSeq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接