RDD的Transformation(转换算子)
RDD整体上分为Value类型、双Value类型和Key-Value类型
1 value类型
创建包名:com.xiao_after.value
1.1 map()映射
1)函数签名:
def map[U: ClassTag](f: T => U): RDD[U]
2)功能说明:参数 f 是一个函数,它可以接收一个参数。当某个RDD执行map方法时,
会遍历该RDD中的每一个数据项,并依次应用 f 函数,从而产生一个新的RDD。即,这个
新的RDD中的每一个元素都是原来RDD中每一个元素依次应用 f 函数而得到的。
3)需求说明:创建一个1-4数组的RDD,两个分区,将所有元素*2形成新的RDD
4)具体实现
package com.xiao_after.value
import org.apache.spark.{
SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* @author xiaohu
* @create 2020-09-24 21:06
*/
object value01_map {
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] = sc.makeRDD(1 to 4, 2)
//3.2 调用map方法,每个元素乘以2
val rdd1: RDD[Int] = rdd.map(_ * 2)
//3.3 遍历打印输出rdd1
rdd1.collect().foreach(println)
//4.关闭连接
sc.stop()
}
}
5)输出结果
2
4
6
8
1.2 mapPartitions()以分区为单位执行Map
1)函数签名:
def mapPartitions[U: ClassTag](f: Iterator[T] => Iterator[U]
2)功能说明:Map是依次处理一个元素,而mapPartitions一次处理一个分区数据。
3)需求说明:创建一个RDD,4个元素,2个分区,使每个元素组成新的RDD。
4)具体实现
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 21:16
*/
object value02_mapPartitions {
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] = sc.makeRDD(1 to 4,2)
//3.2 调用mapPartitions方法,每个元素乘以2
val rdd1: RDD[Int] = rdd.mapPartitions(_.map(_*2))
//3.3 遍历打印rdd1中的数据
rdd1.collect().foreach(println)
//4.关闭连接
sc.stop()
}
}
5)输出结果
2
4
6
8
1.3 map()和mapPartitions()区别
1)map():每次处理一条数据;
2)mapPartitions():每次处理一个分区的数据,这个分区的数据处理完后,元RDD分区
中的数据才能释放,可能导致OOM;
3)开发经验:当内存空间较大的时候,建议使用 mapPartitions(),以提高处理效率。
1.4 mapPartitionsWithIndex()带分区号
1)函数签名
def mapPartitionsWithIndex[U: ClassTag](
f: (Int, Iterator[T]) => Iterator[U],
preservesPartitioning: Boolean = false): RDD[U]
2)功能说明:类似于mapPartitions,比mapPartitions多一个整数参数表示分区号
3)需求说明:创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的
RDD
4)具体实现
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 21:07
*/
object value03_mapPartitionsWithIndex {
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] = sc.makeRDD(1 to 4, 2)
//3.2 使每个元素跟所在分区号形成一个元组,组成一个新的RDD
val rdd1: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, items) => {
items.map((index, _))
})
//3.3 遍历打印rdd1的数据
rdd1.collect().foreach(println)
//4.关闭连接
sc.stop()
}
}
5)输出结果
(0,1)
(0,2)
(1,3)
(1,4)
1.5 flatMap()扁平化
1)函数签名
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
2)功能说明
与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到
RDD中。区别:在flatMap操作中,f函数的返回值是一个集合,并且会将每一个该集合中
的元素拆分出来放到新的RDD中;
3)需求说明:创建一个集合,集合里面存储的还是子集合,把所有子集合中数据取出放
入到一个大的集合中;
4)具体实现
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 22:55
*/
object value04_flatMap {
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[List[Int]] = sc.makeRDD(List(List(1, 2), List(3, 4), List(5, 6), List(7)), 2)
//3.2 把所有子集合中数据取出放入到一个大的集合中
val rdd1: RDD[Int] = rdd.flatMap(list => list)
//3.3 遍历打印rdd1的数据
rdd1.collect().foreach(println)
//4.关闭连接
sc.stop()
}
}
5)运行结果
1
2
3
4
5
6
7
1.6 glom()分区转换数组
1)函数签名
def glom(): RDD[Array[T]]
2)功能说明:
该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与
原分区中元素类型一致
3)需求说明:创建一个2个分区的RDD,并将每个分区的数据放到一个数组,求出每个
分区的最大值
4)具体实现
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 22:56
*/
object value05_glom {
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] = sc.makeRDD(1 to 4, 2)
//3.2 求出每个分区的最大值
val rdd1: RDD[Int] = rdd.glom().map(_.max)
//3.3 求出所有分区的最大值的和 2 + 4
println(rdd1.collect().sum)
//4.关闭连接
sc.stop()
}
}
5)运行结果
6
1.7 groupBy()分组
1)函数签名:
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
2)功能说明:分组,按照传入函数的返回值进行分组。将相同的 key 对应的值放入一个
迭代器
3)需求说明:创建一个RDD,按照元素模以2的值进行分组
4)具体实现
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 22:56
*/
object value06_groupby {
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] = sc.makeRDD(1 to 4, 2)
//3.2 将每个分区的数据放到一个数组并收集到Driver端打印
rdd.groupBy(_ % 2).collect().foreach(println)
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)
//4.关闭连接
sc.stop()
}
}
5)运行结果
(0,CompactBuffer(2, 4))
(1,CompactBuffer(1, 3))
--------------------------
(s,CompactBuffer(spark, scala))
(h,CompactBuffer(hello, hive, hadoop))
注:groupBy会存在shuffle过程;
shuffle:将不同的分区数据进行打乱重组的过程;
shuffle一定会落盘。可以在local模式下执行程序,通过4040看效果。
1.8 GroupBy之WordCount
1)需求说明:创建一个RDD,使用 GroupBy 实现 WordCount 功能
2)具体实现:
package com.xiao_after.value
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author xiaohu
* @create 2020-09-24 23:37
*/
object value07_groupby_wordcount {
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 strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World")
val rdd = sc.makeRDD(strList)
//3.2 将字符串拆分成一个一个的单词
val rdd1: RDD[String] = rdd.flatMap(_.split(" "))
//3.3 将单词结果进行转换,(word) => (word,1)
val rdd2: RDD[(String, Int)] = rdd1.map((_, 1))
//3.4 将转换结构后的数据分组
val rdd3: RDD[(String, Iterable[(String