如何将java.util.List转换为Scala的List?

2021-12-08 20:02:38.0

因为Scala API和Java API可以互操作,所以在使用Scala API开发Spark应用时,我们经常会遇到这样的场景:调用Java的API库/包,返回的是java.util.List类型,但是我们需要的是Scala的List类型(scala.collection.immutable.List)。

换句话说,如何把java.util.List类型转换为Scala的List类型?

首先,请看下面这个示例场景。在这个场景中,我们使用结巴分词包对一个字符串进行分词(你可以理解为这个字符串是DataFrame中的一个属性):

import com.huaban.analysis.jieba.JiebaSegmenter

object Test {
  def main(args: Array[String]): Unit = {
    val feature = "正品相宜本草黑茶男士清爽骄阳防晒露护理防晒霜控油保湿护肤品"

    // 默认
    val words: java.util.List[String] = new JiebaSegmenter().sentenceProcess(feature)  

    // 输出
    words.forEach(println)
  }
}

在上面的代码中,我们对feature字符串变量进行分词,得到的结果是java.util.List类型,因为结巴分词源码包中是用java语言开发的,所以这里必须将变量words声明为java.util.List[String]类型。

执行以上代码,可以看到输出如下这样的分词结果:

正品
相宜
本草
黑茶
男士
清爽
骄阳
防晒露
护理
防晒霜
控油
保湿
护肤品

但是这里存在着这样一个问题:Spark DataFrame并不支持java.util.List类型。所以单就这个示例代码执行没有问题,但是在Spark SQL中用于对DataFrame属性列进行分词,就会抛出异常。在这种情形下,可以有两种解决办法:

  • 将返回的java.util.List转换为String类型(调用toString()方法);
  • 将返回的java.util.List转换为Scala的List[String]类型。

第一种方法我们在此不再赘述。我们只研究第二种方式,如何将java.util.List转换为Scala的List[String]类型?

可以简单地使用Scala的scala.collection.JavaConverters类来进行转换,如下所示:

import com.huaban.analysis.jieba.JiebaSegmenter
import scala.collection.JavaConverters._

object Test {
  def main(args: Array[String]): Unit = {
    val feature = "正品相宜本草黑茶男士清爽骄阳防晒露护理防晒霜控油保湿护肤品"

    // 从java.util.List 转换为 scala.collection.immutable.List
    // val words: scala.collection.immutable.List[String] = new JiebaSegmenter().sentenceProcess(feature).asScala.toList
    // 可简化如下
    val words = new JiebaSegmenter().sentenceProcess(feature).asScala.toList

    words.foreach(println)
  }
}

需要注意的是,从Scala 2.13开始, scala.collection.JavaConverters包被标记为弃用,请使用scala.jdk.CollectionConverters包。例如:

import com.huaban.analysis.jieba.JiebaSegmenter
import scala.jdk.CollectionConverters._      // 注意这里的变化

object Test {
  def main(args: Array[String]): Unit = {
    val feature = "正品相宜本草黑茶男士清爽骄阳防晒露护理防晒霜控油保湿护肤品"

    // 从java.util.List 转换为 scala.collection.immutable.List
    val words = new JiebaSegmenter().sentenceProcess(feature).asScala.toList

    words.foreach(println)
  }
}

这时返回的words就是一个Scala List,可直接作为DataFrame的属性类型。


《Spark原理深入与编程实战》