[Scala源码] Scala与Java间集合类型转换的性能分析

JavaConverters

如何使用

引入依赖

arduino 复制代码
import collection.JavaConverters._

集合显式调用 asJava ,asScala进行转化

在代码中经常会涉及scala和java集合的相互转换,担心其会产生性能上的问题,今天就来看看源码分析一下转换过程带来的代价。

先说结论:适配器模式(Adapter Pattern)实现,时间复杂度O(1),只会带来很小的性能开销。同时Java集合转为Scala后再转回Java会得到原来的对象,而非Wrapper套Wrapper

如何实现(源码分析)

以List转换为例

scala => java

测试代码:

ini 复制代码
object JavaConverterTest {
  def main(args: Array[String]): Unit = {
    import collection.JavaConverters._
    val list = List[String]("A", "B", "C")
    val jListWrapper = list.asJava
    println(jListWrapper)
  }
}

借助CFR反编译得到:在线反编译工具网址

java 复制代码
/*
 * Decompiled with CFR 0.150.
 * 
 * Could not load the following classes:
 *  scala.Predef$
 *  scala.collection.JavaConverters$
 *  scala.collection.Seq
 *  scala.collection.immutable.List
 *  scala.collection.immutable.List$
 */
import scala.Predef$;
import scala.collection.JavaConverters$;
import scala.collection.Seq;
import scala.collection.immutable.List;
import scala.collection.immutable.List$;

public final class JavaConverterTest$ {
    public static final JavaConverterTest$ MODULE$;

    public static {
        new JavaConverterTest$();
    }

    public void main(String[] args) {
        List list = List$.MODULE$.apply((Seq)Predef$.MODULE$.wrapRefArray((Object[])new String[]{"A", "B", "C"}));
        Predef$.MODULE$.println(JavaConverters$.MODULE$.seqAsJavaListConverter((Seq)list).asJava());
    }

    private JavaConverterTest$() {
        MODULE$ = this;
    }
}

根据反编译结果可以发现调用了JavaConverters的方法seqAsJavaListConverter

JavaConverters

scala 复制代码
object JavaConverters extends DecorateAsJava with DecorateAsScala

JavaConverters的方法全部继承于DecorateAsJava以及DecorateAsScala

DecorateAsJava

DecorateAsJava定义了一系列隐式转换方法

关于隐式转换方法,个人的理解就是:提供了一系列类型间的转换供编译器选择。当编译不成功时,编译器会去找合适的隐式方法将类型进行适当的转换从而使得编译通过。

DecorateAsJava的方法seqAsJavaListConverter

scala 复制代码
  implicit def seqAsJavaListConverter[A](b : Seq[A]): AsJava[ju.List[A]] =
    new AsJava(seqAsJavaList(b))

便是提供了从:Seq[A] => AsJava[ju.List[A]]的隐式类型转换,从而提供了asJava的方法进行显示的转换

Decorators

AsJava对象是Decorators的内部类

scala 复制代码
private[collection] object Decorators {
	class AsJava[A](op: => A) {
 	 /** Converts a Scala collection to the corresponding Java collection */
	  def asJava: A = op
	}
	//其他内部类
}

op: => A: 这里不是传了个方法(有冒号),而是是名调用(call-by-name), 这个参数op实际上指代一个块(代码),且这个块的返回值为A,在这里也就是AsJava的泛型ju.List[A]

这里可以看出:具体将scala集合List转化为util.List的逻辑在seqAsJavaList方法里

AsJavaConverters

scala 复制代码
def seqAsJavaList[A](s: Seq[A]): ju.List[A] = s match {
  case null                   => null
  case JListWrapper(wrapped)  => wrapped.asInstanceOf[ju.List[A]]
  case _                      => new SeqWrapper(s)
}

这里按Seq类型进行了分类:

  • JListWrapper(wrapped): 本身就是Java List包装成的Scala List => 去掉包装即可
  • 其他非null情况 => new SeqWrapper(s) (将scala List包装为Java List)

Wrapper

点进SeqWrapper,看看具体怎么包装的

scala 复制代码
private[collection] trait Wrappers {
 
  trait IterableWrapperTrait[A] extends ju.AbstractCollection[A] {
    val underlying: Iterable[A]
    def size = underlying.size
    override def iterator = IteratorWrapper(underlying.iterator)
    override def isEmpty = underlying.isEmpty
  }

  case class IteratorWrapper[A](underlying: Iterator[A]) extends ju.Iterator[A] with ju.Enumeration[A] {
    def hasNext = underlying.hasNext
    def next() = underlying.next()
    def hasMoreElements = underlying.hasNext
    def nextElement() = underlying.next()
    override def remove() = throw new UnsupportedOperationException
  }
    
  case class SeqWrapper[A](underlying: Seq[A]) extends ju.AbstractList[A] with IterableWrapperTrait[A] {
    def get(i: Int) = underlying(i)
  }
}

SeqWrapperWrapper下的一个内部样例类,继承了AbstractList,实现了其抽象方法get以及Iterable接口的迭代器相关方法

迭代器相关包装实现:IterableWrapperTraitIteratorWrapper

注:java集合继承关系:

graph BT A(AbstractList 抽象类) ---> B(List 接口) ---> D(Collection 接口) ---> E(Iterater 接口) A ---> C(AbstractCollection 抽象类) ---> D
java => scala

详情见JListWrapper,略。

结论

由源码可以看出:对于列表类型的集合类型转换,时间复杂度为O(1),并不会对性能产生什么影响,只是做了个适配/包装(实现java接口的方法),并不存在集合数据的拷贝。

Map转换的一些点

selfthis的引用,当MapWrapperWrapper内部类的时候,定义的self引用可以将内部类的this和外部类的this进行区分

scala 复制代码
  class MapWrapper[A, B](underlying: Map[A, B]) extends ju.AbstractMap[A, B] with Serializable { self =>
		//一些方法的实现
  }
相关推荐
hopsky6 天前
《Scala 学习手册》核心内容详解
人工智能·学习·scala
abcy07121316 天前
scala playframework 搭建与新建controller教程
scala
lpfasd12317 天前
编程语言榜单变迁分析
开发语言·后端·scala
戮漠summer1 个月前
Missing Semester 计算机教育中缺失的一课 Lecture 01 Shell
开发语言·后端·scala
醉熏的石头1 个月前
Ubuntu 中的编程语言(中)
linux·ubuntu·scala
Irene19912 个月前
Scala、Java、Python、JavaScript 的核心特性和应用场景(Python 的“单机“局限性:GIL 机制导致的多核并行缺陷)
java·javascript·python·scala
李白的天不白2 个月前
确认 Nginx 配置文件是否真的生效
scala
醉颜凉2 个月前
Scala自定义Monad实战:从理论到应用的完整指南
大数据·算法·scala
那晚的她2 个月前
Scala中Set集合
开发语言·后端·scala
IvanCodes2 个月前
二、Scala流程控制:分支与循环
大数据·scala