RDD算子源码《一》返回一个结果的actions算子

返回一个结果的actions算子

目录

 

foreach()

collect()

reduce()

fold()

aggregate()

count()

toArray()

take()

first()


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),没取到的话抛异常。

猜你喜欢

转载自blog.csdn.net/zhaolq1024/article/details/82757668
今日推荐