返回一个结果的actions算子
目录
foreach()
def foreach(f: T => Unit) {
val cleanF = sc.clean(f)
//提交job,迭代器每个元素执行传入方法
sc.runJob(this, (iter: Iterator[T]) => iter.foreach(cleanF))
}
iter.foreach是无返回值,一般用于调试打印RDD中的数据
val a=sc.parallelize(1 to 5,2)
a.foreach(print)
将打印12345
collect()
def collect(): Array[T] = {
val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
//将各分区数据连接起来
Array.concat(results: _*)
}
collect()方法将RDD中的数据转化为数组
val a=sc.parallelize(1 to 5,2)
println(a.collect()(1))//打印下标为1的值
将打印 2
reduce()
def reduce(f: (T, T) => T): T = {
val cleanF = sc.clean(f)
//如果迭代器有值,调用reduceLeft()
val reducePartition: Iterator[T] => Option[T] = iter => {
if (iter.hasNext) {
Some(iter.reduceLeft(cleanF))
}else {
None
}
}
//提交job,runJob返回数组
val options = sc.runJob(this, reducePartition)
val results = new ArrayBuffer[T]
//遍历返回的结果,存入数组中
for (opt <- options; elem <- opt) {
results += elem
}
if (results.size == 0) {
throw new UnsupportedOperationException("empty collection")
} else {
//数组继续调用reduceLeft()方法,将结果返回
return results.reduceLeft(cleanF)
}
}
我们知道,提交task的时候,一个RDD的分区数据对应一个迭代器,一个迭代器对应一个task,
所以,runJob()返回来的数组存放的是各分区的结果。
reduce()主要分两步,第一步调用reduceLeft()计算各个分区数据,第二步,再调用reduceLeft()计算各分区结果,返回。
接下来我们说下Iterator的reduceLeft()方法
val w=(1 to 5).toIterator
println(w.reduceLeft(_+_)) //15
println(w.reduceLeft(_-_)) //-13
println(w.reduceRight(_-_)) //3
reduceLeft()计算的是从左边开始相邻两个元素的值,将结果与后面元素再计算,直到所有元素参与计算
第一个输出:(((1+2)+3)+4)+5=15
第二个输出:(((1-2)-3)-4)-5=-13
第三个输出:从右边开始,1-(2-(3-(4-5)))=3
fold()
def fold(zeroValue: T)(op: (T, T) => T): T = {
val cleanOp = sc.clean(op)
val results = sc.runJob(this, (iter: Iterator[T]) => iter.fold(zeroValue)(cleanOp))
return results.fold(zeroValue)(cleanOp)
}
fold跟reduce是差不多,只不过fold有初值,先计算各个分区与初值的结果,存入数组,再计算结果与初值的值。
可以得出当有1个分区时,初值被计算2次,第一次与分区数据,第二次与分区结果数据。
val a=sc.parallelize(List(1,2,3,4,5),1)
val b=sc.parallelize(List(1,2,3,4,5),2)
val c=sc.parallelize(List(1,2,3,4,5),3)
println(a.fold(10)(_+_))
println(b.fold(10)(_+_))
println(c.fold(10)(_+_))
a的结果为:1+2+3+4+5+10=25, 25+10=35
b的结果为:1+2+3+10=16,4+5+10=19,16+19+10=45 (分区数据编的,与真实情况可能有区别)
c的结果为:1+2+10=13,3+4+10=17,5+10=15,13+17+15=55
aggregate()
def aggregate[U: ClassManifest](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U = {
val cleanSeqOp = sc.clean(seqOp)
val cleanCombOp = sc.clean(combOp)
val results = sc.runJob(this,
(iter: Iterator[T]) => iter.aggregate(zeroValue)(cleanSeqOp, cleanCombOp))
return results.fold(zeroValue)(cleanCombOp)
}
aggregate()先用seqOp方法计算各分区里的数据,将各分区结果存入数组,然后再调用fold(),方法为combOp计算结果数据。
可以看到aggregate()与fold()的区别在于:flod计算某个分区数据与最后计算各分区结果数据用的用同一个方法,而aggregate()
则可以用两种不同的方法,当aggregate()两个方法一样时,结果与fold()是一样的。
val a=sc.parallelize(List(1,2,3,4),2)
println(a.aggregate(5)(_+_,_+_))
println(a.fold(5)(_+_))
结果都为 25,计算步骤可参考上面的fold()方法
val a=sc.parallelize(List(1,2,3,4),2)
println(a.aggregate(5)(_+_,_*_))
结果为 480
计算步骤:假设两个分区数据为(1,2)(3,4)
分区1结果:1+2+5=8 分区2结果:3+4+5=12
最终结果:调用第二个方法计算,8*12*5=480
count()
def count(): Long = {
sc.runJob(this, (iter: Iterator[T]) => {
var result = 0L
while (iter.hasNext) {//遍历迭代器中所有元素
result += 1L
iter.next
}
result
}).sum
}
count()用来求RDD中元素的个数,求出个分区元素个数,然后sum()求和
val w=sc.parallelize(0 to 10,3).count()
println(w)
结果为为 11
toArray()
def toArray(): Array[T] = collect()
调用collect()方法,将RDD转为数组
take()
def take(num: Int): Array[T] = {
if (num == 0) {
return new Array[T](0)
}
val buf = new ArrayBuffer[T]//存放取出的元素的数组
var p = 0
while (buf.size < num && p < splits.size) {//循环,直到数取够或者循环完所有分区
val left = num - buf.size//得到还需要从取出的元素数
//true为允许job本节点执行,无需发往集群
val res = sc.runJob(this, (it: Iterator[T]) => it.take(left).toArray, Array(p), true)
buf ++= res(0)//将从第一个分区取到的数据存入数组
if (buf.size == num)//若已取够,返回,无的话继续取后面的分区
return buf.toArray
p += 1
}
return buf.toArray
}
该方法为取出RDD的n个元素,先从一个分区中取数据,取够的话,直接返回,没够的话,继续取别的分区。
val w=sc.parallelize(0 to 100,2)
for(e<-w.take(10)){
print(e)
}
结果为:0123456789
first()
def first(): T = take(1) match {
case Array(t) => t
case _ => throw new UnsupportedOperationException("empty collection")
}
该方法为取第一个元素,调用take(1),没取到的话抛异常。