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)
}
}
SeqWrapper
是Wrapper
下的一个内部样例类,继承了AbstractList
,实现了其抽象方法get
以及Iterable
接口的迭代器相关方法
迭代器相关包装实现:IterableWrapperTrait
和IteratorWrapper
注:java集合继承关系:
java => scala
详情见JListWrapper
,略。
结论
由源码可以看出:对于列表类型的集合类型转换,时间复杂度为O(1)
,并不会对性能产生什么影响,只是做了个适配/包装(实现java接口的方法),并不存在集合数据的拷贝。
Map转换的一些点
self
是this
的引用,当MapWrapper
为Wrapper
内部类的时候,定义的self
引用可以将内部类的this
和外部类的this
进行区分
scala
class MapWrapper[A, B](underlying: Map[A, B]) extends ju.AbstractMap[A, B] with Serializable { self =>
//一些方法的实现
}