Scala的流的实现在Stream中。
主要用到的实现类是Cons类。
@SerialVersionUID(-602202424901551803L)
final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A] {
override def isEmpty = false
override def head = hd
@volatile private[this] var tlVal: Stream[A] = _
@volatile private[this] var tlGen = tl _
def tailDefined: Boolean = tlGen eq null
override def tail: Stream[A] = {
if (!tailDefined)
synchronized {
if (!tailDefined) {
tlVal = tlGen()
tlGen = null
}
}
tlVal
}
}
从这段代码中可以看出,一个有A类组成的Stream流中,构成的主要参数有两个,一个是流中第一个元素,也就是head,而另一个参数则是一个返回内容为A类的流的方法,将会被转换为函数保存。与此同时,又定义了一个函数tail,则是返回第二个参数方法返回的那个流。以上述一致,返回的流也只包含一个head(也就是流中的第二个元素),和一个产生流的方法。这也是scala中的流是无限的原因。
由此可以得出,scala的流中的数据都是只要调用,才会被产生,一开始只存在流的首部元素,和产生剩余流的一个方法。
关于流的操作以filter()为例子。
override def filter(p: A => Boolean): Stream[A] = {
// optimization: drop leading prefix of elems for which f returns false
// var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
var rest = this
while (!rest.isEmpty && !p(rest.head)) rest = rest.tail
// private utility func to avoid `this` on stack (would be needed for the lazy arg)
if (rest.nonEmpty) Stream.filteredTail(rest, p)
else Stream.Empty
}
filter()函数在被流调用的时候,首先只会去尝试与已经存在的第一个元素进行判断,如果不符合filter的条件,那么将会调用流产生下一个流的函数来产生下一个流,直到新的流的第一个元素符合filter的条件停止循环,可是流中的数据并不是严格有序的,但是如果只在这里结束,并不能保证剩下的流的数据会被过滤,所以要保证剩下的流中的数据也会在产生后被filter进行过滤,这样的操作存在于filteredTail()函数中。
private[immutable] def filteredTail[A](stream: Stream[A], p: A => Boolean) = {
cons(stream.head, stream.tail filter p)
}
在这里,之间会重新创建一个流,第一个元素不变,不同的是这里会把在创建新流的函数中加入刚刚的filter()函数,合并在一起作为新的创建流函数,之后每一个新产生的元素都会紧接着被调用filter()函数进行过滤。