spark_Transformation转换算子

228 阅读10分钟

4.1 Value类型

4.1.1 map映射

分析图

具体实现

object value01_map {
    def main(args: Array[String]): Unit = {
        //1.创建SparkConf并设置App名称
        val conf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
        //2.创建SparkContext,该对象是提交Spark App的入口
        val sc = new SparkContext(conf)
        //3具体业务逻辑
        // 3.1 创建一个RDD
        val rdd: RDD[Int] = sc.makeRDD(1 to 4,2)
        // 3.2 调用map方法,每个元素乘以2
        val mapRdd: RDD[Int] = rdd.map(_ * 2)
        // 3.3 打印修改后的RDD中数据
        mapRdd.collect().foreach(println)
        //4.关闭连接
        sc.stop()
    }
}

4.1.2 mapPartitions以分区为单位执行map

mapPartitions算子

//以分区为单位,对RDD中的元素进行映射
//一般适用于批处理的操作,比如:将RDD中的元素插入到数据库中,需要数据库连接,
// 如果每一个元素都创建一个连接,效率很低;可以对每个分区的元素,创建一个连接

分析图

案例

val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)
// 3.2 调用mapPartitions方法,每个元素乘以2
val rdd1 = rdd.mapPartitions(x=>x.map(_*2))
// 3.3 打印修改后的RDD中数据
rdd1.collect().foreach(println)

4.1.3 map与mapPartitions的区别

4.1.4 mapPartitionsWithIndex 带分区号

//3具体业务逻辑
// 3.1 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)
// 3.2 创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的RDD
val indexRdd = rdd.mapPartitionsWithIndex( (index,items)=>{items.map( (index,_) )} )
//扩展功能:第二个分区元素*2,其余分区不变
// 3.3 打印修改后的RDD中数据
indexRdd.collect().foreach(println)

map相关(个人理解):

无论是map,mapPartition,还是mapPartitionsWithIndex(),经过参数中定义的方法映射处理以后,最后的返回结果都是一个iterator迭代器,经过map的时候,map每次处理一个元素时,都会将处理后的元素放到一个迭代器中,当遍历结束后,迭代器就是最终的结果。

4.1.5 flatmap 压平

个人理解:

flatmap偏平化的处理,适合于所有集合里面的元素,每个元素还是集合,然后将子元素集合中所有的元素全部放到一个集合中,让子集合中的元素时单纯的元素,不在是集合。相当于敲破所有子元素集合,把子元素集合的元素弄出来。

原理图

实例

//3具体业务逻辑
// 3.1 创建一个RDD
val listRDD=sc.makeRDD(List(List(1,2),List(3,4),List(5,6),List(7)), 2)
// 3.2 把所有子集合中数据取出放入到一个大的集合中
listRDD.flatMap(list=>list).collect.foreach(println)

4.1.6 glom分区转换数组

原理图

实例

//3具体业务逻辑
// 3.1 创建一个RDD
val rdd = sc.makeRDD(1 to 4, 2)
// 3.2 求出每个分区的最大值  0->1,2   1->3,4
val maxRdd: RDD[Int] = rdd.glom().map(_.max)
// 3.3 求出所有分区的最大值的和 2 + 4
println(maxRdd.collect().sum)

4.1.7 groupBy分组

个人理解

groupBy:按照传入函数的返回值进行分组,groupBy的参数,相当于是定义一个分组规则

//    val rdd: RDD[Int] = sc.makeRDD(1 to 10, 3)
//    println(rdd.groupBy(_%2).collect().mkString(","))
//    (0,CompactBuffer(2, 4, 6, 8, 10)),(1,CompactBuffer(1, 3, 5, 7, 9))

    val rdd = sc.makeRDD(Array("江西 南昌 上饶", "河北 石家庄 大同 上饶","南昌 大同 上饶"),3)
    val flatMapRdd = rdd.flatMap(_.split(" ")).map((_,1))
//    val str: String = flatMapRdd.groupBy(_.contains("上饶")).collect().mkString(",")
    val str: String = flatMapRdd.groupBy(_._1).collect().mkString(",")
/*
(河北,CompactBuffer((河北,1))),(石家庄,CompactBuffer((石家庄,1))),(南昌,CompactBuffer((南昌,1), (南昌,1))),(江西,CompactBuffer((江西,1))),(上饶,CompactBuffer((上饶,1), (上饶,1), (上饶,1))),(大同,CompactBuffer((大同,1), (大同,1)))
*/

原理图

实例

// 3.1 创建一个RDD
val rdd = sc.makeRDD(1 to 4, 2)
// 3.2 将每个分区的数据放到一个数组并收集到Driver端打印
rdd.groupBy(_ % 2).collect().foreach(println)
// 3.3 创建一个RDD
val rdd1: RDD[String] = sc.makeRDD(List("hello","hive","hadoop","spark","scala"))
// 3.4 按照首字母第一个单词相同分组
rdd1.groupBy(str=>str.substring(0,1)).collect().foreach(println)

**备注:**groupBy会存在shuffle的过程

shuffle:将不同的分区数据打乱重组的过程,shuffle一定会落盘

4.1.8 groupBy之wordCount

// 3.1 创建一个RDD
val strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World")
val rdd = sc.makeRDD(strList)
// 3.2 将字符串拆分成一个一个的单词
val wordRdd: RDD[String] = rdd.flatMap(str=>str.split(" "))
// 3.3 将单词结果进行转换:word=>(word,1)
val wordToOneRdd: RDD[(String, Int)] = wordRdd.map(word=>(word, 1))
// 3.4 将转换结构后的数据分组
val groupRdd: RDD[(String, Iterable[(String, Int)])] = wordToOneRdd.groupBy(t=>t._1)
// 3.5 将分组后的数据进行结构的转换
val wordToSum: RDD[(String, Int)] = groupRdd.map {
    case (word, list) => {
        (word, list.size)
    }
}

wordToSum.collect().foreach(println)

复杂版的wordCount

val rdd: RDD[(String, Int)] = sc.makeRDD(List(("Hello Scala, 2), ("Hello Spark, 3), (Hello World, 2)))

//以下方式是比较好的一种实现方式
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("Hello Scala", 2), ("Hello Spark", 3), ("Hello World", 2)))
//对RDD中的元素进行扁平映射
val flatMapRDD: RDD[(String, Int)] = rdd.flatMap {
  case (words, count) => {
    words.split(" ").map(word => (word, count))
  }
}
//按照单词对RDD中的元素进行分组     (Hello,CompactBuffer((Hello,2), (Hello,3), (Hello,2)))
val groupByRDD: RDD[(String, Iterable[(String, Int)])] = flatMapRDD.groupBy(_._1)
//对RDD的元素重新进行映射
val resRDD: RDD[(String, Int)] = groupByRDD.map {
  case (word, datas) => {
    (word, datas.map(_._2).sum)
  }
}

resRDD.collect().foreach(println)

4.1.9 filter 算子

个人理解

filter其实和groupBy是类似的,groupBy是按照参数中的函数规则,对元素进行分类,filter也是利用每个元素,和定义的方法规则,如果满足规则,也就是true,这些元素将会留下,放进新的RDD中,否则丢弃不满足条件的数据

原理图

实例:

val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4),2)
//过滤出符合条件的数据
val filterRdd: RDD[Int] = rdd.filter(_ % 2 == 0)

4.1.10 sample 采样 ?

原理图

实例

// 3.1 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(1 to 10)
// 3.2 打印放回抽样结果
rdd.sample(true, 0.4, 2).collect().foreach(println)
// 3.3 打印不放回抽样结果
rdd.sample(false, 0.2, 3).collect().foreach(println)

扩展小结:

object Transformation_sample {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交Spark App的入口
    val sc: SparkContext = new SparkContext(conf)


    //val rdd: RDD[Int] = sc.makeRDD(1 to 10)

    /*
      -withReplacement  是否抽样放回
        true    抽样放回
        false   抽样不放回
      -fraction
        withReplacement=true   表示期望每一个元素出现的次数  >0
        withReplacement=false  表示RDD中每一个元素出现的概率[0,1]
      -seed 抽样算法初始值
        一般不需要指定
    */
    //从rdd中随机抽取一些数据(抽样放回)
    //val newRDD: RDD[Int] = rdd.sample(true,3)


    //从rdd中随机抽取一些数据(抽样不放回)
    //val newRDD: RDD[Int] = rdd.sample(false,0.6)
    //newRDD.collect().foreach(println)


    val stds: List[String] = List("张三""lisi","王五")

    val nameRDD: RDD[String] = sc.makeRDD(stds)

    //从上面RDD中抽取一名幸运同学进行连麦

    val luckyMan: Array[String] = nameRDD.takeSample(false,1)

    luckyMan.foreach(println)


    // 关闭连接
    sc.stop()
  }
}

4.1.11 distinct 算子

源码

  def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
    map(x => (x, null)).reduceByKey((x, y) => x, numPartitions).map(_._1)
  }

原理图

实例

// 3.1 创建一个RDD
val distinctRdd: RDD[Int] = sc.makeRDD(List(1,2,1,5,2,9,6,1))
// 3.2 打印去重后生成的新RDD
distinctRdd.distinct().collect().foreach(println)
// 3.3 对RDD采用多个Task去重,提高并发度
distinctRdd.distinct(2).collect().foreach(println)

4.1.12 coalesce 重新分区

源码:

def coalesce(numPartitions: Int, shuffle: Boolean = false,
             partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
            (implicit ord: Ordering[T] = null)
    : RDD[T] = withScope {
  require(numPartitions > 0, s"Number of partitions ($numPartitions) must be positive.")
  //默认是不执行shuffle,一般用于缩减分区
  if (shuffle) {
    /** Distributes elements evenly across output partitions, starting from a random partition. */
    val distributePartition = (index: Int, items: Iterator[T]) => {
      var position = (new Random(index)).nextInt(numPartitions)
      items.map { t =>
        // Note that the hash code of the key will just be the key itself. The HashPartitioner
        // will mod it with the number of total partitions.
        position = position + 1
        (position, t)
      }
    } : Iterator[(Int, T)]

    // include a shuffle step so that our upstream tasks are still distributed
    new CoalescedRDD(
      new ShuffledRDD[Int, T, T](mapPartitionsWithIndex(distributePartition),
      new HashPartitioner(numPartitions)),
      numPartitions,
      partitionCoalescer).values
  } else {
    new CoalescedRDD(this, numPartitions, partitionCoalescer)
  }
}

小结:

  • -coalesce

    ​ 默认是不执行shuffle,一般用于缩减分区

  • -repartition

    ​ 底层调用的就是coalesce,只不过默认是执行shuffle,一般用于扩大分区

    def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
      coalesce(numPartitions, shuffle = true)
    }
    

//缩减分区 //val newRDD: RDD[Int] = numRDD.coalesce(2)

//扩大分区 //注意:默认情况下,如果使用coalesce扩大分区是不起作用的 。因为底层没有执行shuffle //val newRDD: RDD[Int] = numRDD.coalesce(4)

Coalesce算子包括:配置执行Shuffle和配置不执行Shuffle两种方式。

4.1.12.1 不执行shuffle方式

//3.创建一个RDD
//val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4), 4)
//3.1 缩减分区
//val coalesceRdd: RDD[Int] = rdd.coalesce(2)
//4. 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 3)
//4.1 缩减分区
val coalesceRdd: RDD[Int] = rdd.coalesce(2)

4.1.12.2 执行shuffle方式

//3. 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 3)
//3.1 执行shuffle
val coalesceRdd: RDD[Int] = rdd.coalesce(2, true)

4.1.12.3 shuffle

4.1.13 repartition 重新分区

//3. 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6), 3)
//3.1 缩减分区
//val coalesceRdd: RDD[Int] = rdd.coalesce(2,true)
//3.2 重新分区
val repartitionRdd: RDD[Int] = rdd.repartition(2)

4.1.14 coalesce和repairtition的区别

1)coalesce重新分区,可以选择是否进行shuffle过程。由参数shuffle: Boolean = false/true决定。

2)repartition实际上是调用的coalesce,进行shuffle。源码如下:

def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
    coalesce(numPartitions, shuffle = true)
}

3)coalesce一般为缩减分区,如果扩大分区,不使用shuffle是没有意义的,repartition扩大分区执行shuffle。

4.1.15 sortBy排序

原理图

sortByKey:

按照k做排序,默认是正序,true-正序,false-倒叙

利用sortByKey倒序

val rdd = sc.makeRDD(List("java is best php is scala is best language"))
val tuples = rdd.flatMap(x => {
  x.split(" ").map((_, 1))
}).groupBy(_._1).map({
  case (index, datas) => {
    (index, datas.size)
  }
}).map(x=>(x._2,x._1)).sortByKey(false)

实例

//3具体业务逻辑
// 3.1 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(List(2, 1, 3, 4, 6, 5))
// 3.2 默认是升序排
val sortRdd: RDD[Int] = rdd.sortBy(num => num)
sortRdd.collect().foreach(println)
// 3.3 配置为倒序排
val sortRdd2: RDD[Int] = rdd.sortBy(num => num, false)
sortRdd2.collect().foreach(println)
// 3.4 创建一个RDD
val strRdd: RDD[String] = sc.makeRDD(List("1", "22", "12", "2", "3"))
// 3.5 按照字符的int值排序
strRdd.sortBy(num => num.toInt).collect().foreach(println)
// 3.5 创建一个RDD
val rdd3: RDD[(Int, Int)] = sc.makeRDD(List((2, 1), (1, 2), (1, 1), (2, 2)))
// 3.6 先按照tuple的第一个值排序,相等再按照第2个值排
rdd3.sortBy(t=>t).collect().foreach(println)

4.1.16 pipe调用脚本

4.2 双Value类型交互

4.2.1 union 并集

//3.1 创建第一个RDD
val rdd1: RDD[Int] = sc.makeRDD(1 to 4)
//3.2 创建第二个RDD
val rdd2: RDD[Int] = sc.makeRDD(4 to 8)
//3.3 计算两个RDD的并集
rdd1.union(rdd2).collect().foreach(println)

4.2.2 subtract 差集

//3.1 创建第一个RDD
val rdd: RDD[Int] = sc.makeRDD(1 to 4)
//3.2 创建第二个RDD
val rdd1: RDD[Int] = sc.makeRDD(4 to 8)
//3.3 计算第一个RDD与第二个RDD的差集并打印
rdd.subtract(rdd1).collect().foreach(println)

4.2.3 intersection 交集

//3.1 创建第一个RDD
val rdd1: RDD[Int] = sc.makeRDD(1 to 4)
//3.2 创建第二个RDD
val rdd2: RDD[Int] = sc.makeRDD(4 to 8)
//3.3 计算第一个RDD与第二个RDD的差集并打印
rdd1.intersection(rdd2).collect().foreach(println)

4.2.4 zip拉链

//3.1 创建第一个RDD
val rdd1: RDD[Int] = sc.makeRDD(Array(1,2,3),3)
//3.2 创建第二个RDD
val rdd2: RDD[String] = sc.makeRDD(Array("a","b","c"),3)
//3.3 第一个RDD组合第二个RDD并打印
rdd1.zip(rdd2).collect().foreach(println)
//3.4 第二个RDD组合第一个RDD并打印
rdd2.zip(rdd1).collect().foreach(println)
//3.5 创建第三个RDD(与1,2分区数不同)
val rdd3: RDD[String] = sc.makeRDD(Array("a","b"),3)
//3.6 元素个数不同,不能拉链
// Can only zip RDDs with same number of elements in each partition
rdd1.zip(rdd3).collect().foreach(println)
//3.7 创建第四个RDD(与1,2分区数不同)
val rdd4: RDD[String] = sc.makeRDD(Array("a","b","c"),2)
//3.8 分区数不同,不能拉链
// Can't zip RDDs with unequal numbers of partitions: List(3, 2)
rdd1.zip(rdd4).collect().foreach(println)

备注:

​ 元素个数不同,不能拉链

​ 分区数不同,不能拉链

小结:

val rdd = sc.makeRDD(1 to 4)
val rdd1 = sc.makeRDD(3 to 6)
//    println(rdd.union(rdd1).collect().mkString(",")) //并集  2个RDD所有元素
//    println(rdd.subtract(rdd1).collect().mkString(",")) //差集 rdd-rdd1,rdd剩余元素
//    println(rdd.intersection(rdd1).collect().mkString(",")) //交集 rdd与rdd1都有的元素
println(rdd.zip(rdd1).collect().mkString(",")) //拉链 组成(k,v)

4.3 key-value类型

4.3.1 partitionBy 按k重新分区

0)理解:

默认分区器是HashPartitioner,自定义的分区器,需要继承Partitioner,重写getPartition方法,该方法就是定义key按照里面的规则分配到指定的分区,该方法返回值就是分区号,也就是对应key分配的分区。

1)原理图

2)实例

val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1,"aaa"),(2,"bbb"),(3,"ccc")),3)
//3.2 对RDD重新分区
val rdd2: RDD[(Int, String)] = rdd.partitionBy(new org.apache.spark.HashPartitioner(2))
//3.3 查看新RDD的分区数
println(rdd2.partitions.size)

3)HashPartitioner源码解读:

class HashPartitioner(partitions: Int) extends Partitioner {
    require(partitions >= 0, s"Number of partitions ($partitions) cannot be negative.")
    
    def numPartitions: Int = partitions
    
    def getPartition(key: Any): Int = key match {
        case null => 0
        case _ => Utils.nonNegativeMod(key.hashCode, numPartitions)
    }
    
    override def equals(other: Any): Boolean = other match {
        case h: HashPartitioner =>
            h.numPartitions == numPartitions
        case _ =>
            false
    }
    
    override def hashCode: Int = numPartitions
}

4) 自定义分区器

object KeyValue01_partitionBy {

    def main(args: Array[String]): Unit = {

        //1.创建SparkConf并设置App名称
        val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

        //2.创建SparkContext,该对象是提交Spark App的入口
        val sc: SparkContext = new SparkContext(conf)

        //3具体业务逻辑
        //3.1 创建第一个RDD
        val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc")), 3)
        //3.2 自定义分区
        val rdd3: RDD[(Int, String)] = rdd.partitionBy(new MyPartitioner(2))

        //4 打印查看对应分区数据
        val indexRdd: RDD[(Int, String)] = rdd3.mapPartitionsWithIndex(
            (index, datas) => {
                // 打印每个分区数据,并带分区号
                datas.foreach(data => {
                    println(index + "=>" + data)
                })
                // 返回分区的数据
                datas
            }
        )

        indexRdd.collect()

        //5.关闭连接
        sc.stop()
    }
}

// 自定义分区
class MyPartitioner(num: Int) extends Partitioner {
    // 设置的分区数
    override def numPartitions: Int = num

    // 具体分区逻辑
    override def getPartition(key: Any): Int = {

        if (key.isInstanceOf[Int]) {

            val keyInt: Int = key.asInstanceOf[Int]
            if (keyInt % 2 == 0)
                0
            else
                1
        }else{
            0
        }
    }
}

4.3.2 reduceByKey 按K重新分区

原理图

看源码可以知道,它底层调用的是combineByKeyWithClassTag,并有combiner操作,分区内和分区间执行相同的业务逻辑。

/*  - `createCombiner`, which turns a V into a C (e.g., creates a one-element list)
*  - `mergeValue`, to merge a V into a C (e.g., adds it to the end of a list)
*  - `mergeCombiners`, to combine two C's into a single one.
*/

案例:

//3.1 创建第一个RDD
val rdd = sc.makeRDD(List(("a",1),("b",5),("a",5),("b",2)))
//3.2 计算相同key对应值的相加结果
val reduce: RDD[(String, Int)] = rdd.reduceByKey((x,y) => x+y)
//3.3 打印结果
reduce.collect().foreach(println

4.3.3 groupByKey 按k重新分组

原理图

看源码可以得出:

它没有进行combiner操作,分区内相同key的v,放进一个CompactBuffer里面,分区间的CompactBuffer进行合并,组成一个大的CompactBuffer,而且CompactBuffer放的都是每个key里面的value

需求说明:创建一个pairRDD,将相同key对应值聚合到一个seq中,并计算相同key对应值的相加结果

//3.1 创建第一个RDD
val rdd = sc.makeRDD(List(("a",1),("b",5),("a",5),("b",2)))
//3.2 将相同key对应值聚合到一个Seq中
val group: RDD[(String, Iterable[Int])] = rdd.groupByKey()
//3.3 打印结果
group.collect().foreach(println)
//3.4 计算相同key对应值的相加结果
group.map(t=>(t._1,t._2.sum)).collect().foreach(println)

4.3.4 reduceByKey与groupByKey的区别

1)reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]。

2)groupByKey:按照key进行分组,直接进行shuffle。

3)开发指导:在不影响业务逻辑的前提下,优先选用reduceByKey。求和操作不影响业务逻辑,求平均值影响业务逻辑。

4.3.5 aggregateByKey 分区内与分区间

按照key处理分区内和分区间的逻辑

原理分析图

案例分析

//3.1 创建第一个RDD
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 3), ("a", 2), ("c", 4), ("b", 3), ("c", 6), ("c", 8)), 2)
//3.2 取出每个分区相同key对应值的最大值,然后相加
rdd.aggregateByKey(0)(math.max(_, _), _ + _).collect().foreach(println)

4.3.6 foldByKey 分区内与分区间相同

和aggregateByKey不同的是,它分区内与分区间的逻辑是相同的

实例:

//3.1 创建第一个RDD
val list: List[(String, Int)] = List(("a",1),("a",1),("a",1),("b",1),("b",1),("b",1),("b",1),("a",1))
val rdd = sc.makeRDD(list,2)
//3.2 求wordcount
//rdd.aggregateByKey(0)(_+_,_+_).collect().foreach(println)
rdd.foldByKey(0)(_+_).collect().foreach(println)

4.3.7 combinByKey

不好理解,要多去理解

原理

案例分析

需求说明:创建一个pairRDD,根据key计算每种key的均值。(先计算每个key对应值的总和以及key出现的次数,再相除得到结果)

//3.1 创建第一个RDD
val list: List[(String, Int)] = List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98))
val input: RDD[(String, Int)] = sc.makeRDD(list, 2)
//3.2 将相同key对应的值相加,同时记录该key出现的次数,放入一个二元组
val combineRdd: RDD[(String, (Int, Int))] = input.combineByKey(
    (_, 1),
    (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
    (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
)
//3.3 打印合并后的结果
combineRdd.collect().foreach(println)
//3.4 计算平均值
combineRdd.map {
    case (key, value) => {
        (key, value._1 / value._2.toDouble)
    }
}.collect().foreach(println)

4.3.8 上面几个聚合的区别

reduceByKey, aggregateByKey, foldByKey和combinByKey

4.3.9 sortByKey 按k排序

实例:

//3.1 创建第一个RDD
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))

//3.2 按照key的正序(默认顺序)
rdd.sortByKey(true).collect().foreach(println)

//3.3 按照key的倒序
rdd.sortByKey(false).collect().foreach(println)

4.3.10 mapValues 只对v进行操作

相当于对一个RDD数据集进行遍历,对每个元素中的value做操作

原理图

实例

//3.1 创建第一个RDD
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (1, "d"), (2, "b"), (3, "c")))
//3.2 对value添加字符串"|||"
rdd.mapValues(_ + "|||").collect().foreach(println)

4.3.11 join 连接

将相同key对应的多个value关联在一起

//3.1 创建第一个RDD
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c")))
//3.2 创建第二个pairRDD
val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (4, 6)))
//3.3 join操作并打印结果
rdd.join(rdd1).collect().foreach(println)

4.3.12 cogroupBy

类似全连接,但是在同一个RDD中对key进行聚合

操作两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合

//3.1 创建第一个RDD
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1,"a"),(2,"b"),(3,"c")))
//3.2 创建第二个RDD
val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1,4),(2,5),(3,6)))
//3.3 cogroup两个RDD并打印结果
rdd.cogroup(rdd1).collect().foreach(println)