RDD整体上分为Value类型、双Value类型和Key-Value类型
一、Value类型
1.map()映射
1)函数签名:
def map[U: ClassTag](f: T => U): RDD[U]
def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}
private[spark] class MapPartitionsRDD[U: ClassTag, T: ClassTag](
......
override def getPartitions: Array[Partition] = firstParent[T].partitions
......
}
protected[spark] def firstParent[U: ClassTag]: RDD[U] = {
dependencies.head.rdd.asInstanceOf[RDD[U]]
}
2)功能说明
参数f是一个函数,它可以接收一个参数。当某个RDD执行map方法时,会遍历该RDD中的每一个数据项,并依次应用f函数,从而产生一个新的RDD。即,这个新RDD中的每一个元素都是原来RDD中每一个元素依次应用f函数而得到的。
3)需求说明:
创建一个1-4数组的RDD,两个分区,将所有元素*2形成新的RDD
4)代码实现
/**
* map
* 对RDD中的元素进行映射
*/
object Spark06_Transformation_map {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4) ,2)
println("原分区" + rdd.partitions.size)
//val newRdd: RDD[Int] = rdd.map((x:Int) =>{ x * 2})
//简化
val newRdd: RDD[Int] = rdd.map(_ * 2)
println("新分区" + newRdd.partitions.size)
newRdd.collect().foreach(println)
//释放资源
sc.stop()
}
}
2.mapPartitions()以分区为单位执行Map
1)函数签名:
def mapPartitions[U: ClassTag](f: Iterator[T] => Iterator[U],preservesPartitioning: Boolean =false):RDD[U]
f函数把每一个分区的数据分别放入到迭代器中,批处理
preservesPartitioning:是否保留上游RDD的分区信息,默认false
2)功能说明:
Map是一次处理一个元素,而mapPartitions一次处理一个分区数据。
3)需求说明:
创建一个RDD,4个元素,2个分区,使每个元素*2组成新的RDD
4)代码实现
/**
* 转换算子mapPartitions
* 以分区为单位,对RDD中的元素进行映射
*/
object Spark02_Transformation_mapPartitons {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
//以分区为单位,对RDD中的元素进行映射
//一般适用于批量处理的操作,例如:将RDD中的元素插入到数据库中,需要连接数据库,如果每一个元素都创建一次连接,效率低下。可以对每个分区的元素创建连接插入
/*rdd.mapPartitions(dates =>
dates.map(_ * 2)
).foreach(println)
*/
//简化
rdd.mapPartitions(_.map(_ * 2)).foreach(println)
//释放资源
sc.stop()
}
}
3.map()和mapPartitions()区别
1)map():
每次处理一条数据。
2)mapPartition():
每次处理一个分区的数据,这个分区的数据处理完后,原RDD中分区的数据才能释放,可能导致OOM。
3)当内存空间较大的时候建议使用mapPartition(),以提高处理效率。
4.mapPartitionsWithIndex()带分区号
1)函数签名:
def mapPartitionsWithIndex[U: ClassTag]( f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false): RDD[U]
//Int表示分区编号
2)功能说明:
类似于mapPartitions,比mapPartitions多一个整数参数表示分区号
3)需求说明:
创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的RDD
每个分区只调用一次f函数,得到RDD2
4)代码实现
/**
* 转换算子mapPartitionsWithIndex
* 以分区为单位,对RDD中的元素进行映射,并带有分区编号
* 可以根据分区号,对某个分区做操作
*/
object Spark03_Transformation_mapPartitonsWithIndex {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8), 5)
//val newRdd: RDD[Int] = rdd.map(a => a * 2)
//val newRdd: RDD[Int] = rdd.mapPartitions(a => a.map(b => b * 2))
//创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的RDD
/*val newRDD: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, datas) => {
datas.map((index, _))
})
*/
//需求:第二个分区的数据*2,其它分区数据不变
val newRDD: RDD[Int] = rdd.mapPartitionsWithIndex(
(index, datas) => {
index match {
case 1 => datas.map(_ * 2)
case _ => datas
}
}
)
newRDD.collect().foreach(println)
//释放资源
sc.stop()
}
}
5.flatMap
1)函数签名:
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
2)功能说明:
与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中。 区别:在flatMap操作中,f函数的返回值是一个集合,并且会将每一个该集合中的元素拆分出来放到新的RDD中。
3)需求说明:
创建一个集合,集合里面存储的还是子集合,把所有子集合中数据取出放入到一个大的集合中。
/**
* 转换算子flatmap将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中。
*/
object Spark01_Transformation_flatmap {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
val rdd: RDD[List[Int]] = sc.makeRDD(List(List(1, 2), List(3,4),List(5,6),List(7,8)),2)
//匿名函数输入输出相同不能简化
val newRdd: RDD[Int] = rdd.flatMap(datas => datas)
newRdd.collect().foreach(println)
//释放资源
sc.stop()
}
}
6.glom()分区转换数组
1)函数签名:
def glom(): RDD[Array[T]]
2)功能说明:
该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与原分区中元素类型一致
3)需求说明:
创建一个2个分区的RDD,并将每个分区的数据放到一个数组,求出每个分区的最大值
4)代码实现
/**
* 转换算子glom
* 将RDD中的每个分区变为一个数组,放置到新的RDD中,数组中元素的类型与原分区中的元素类型一致
*/
object Spark04_Transformation_glom {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6), 2)
println("--------------------------glom前---------------------------")
rdd.mapPartitionsWithIndex((index,datas)=>{
println(index+"---"+ datas.mkString(" "))
datas
}).collect()
println("--------------------------glom后---------------------------")
val newRDD: RDD[Array[Int]] = rdd.glom()
newRDD.mapPartitionsWithIndex((index,datas)=>{
//查看数组中元素使用next()函数
println(index+"---"+ datas.next().mkString(" "))
datas
}).collect()
//释放资源
sc.stop()
}
}
7.groupBy()分组
1)函数签名:
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
2)功能说明:
分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。
3)需求说明:
创建一个RDD,按照元素模以2的值进行分组。
4)代码实现:
/**
* 转换算子groupby
* 按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器
*/
object Spark05_Transformation_groupby {
def main(args: Array[String]): Unit = {
//创建Spark配置文件对象
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_mem").setMaster("local[*]")
//创建SparkContext对象,该对象时提交Spark App的入口
val sc = new SparkContext(conf)
/*val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,6,7,8,9),3)
println("==============groupBy分组前================")
rdd.mapPartitionsWithIndex(
(index,datas)=>{
println(index + "---->" + datas.mkString(","))
datas
}
).collect()
val newRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(_%2)
println("==============groupBy分组后================")
newRDD.mapPartitionsWithIndex(
(index,datas)=>{
println(index + "---->" + datas.mkString(","))
datas
}
).collect()
*/
val rdd: RDD[String] = sc.makeRDD(List("hello", "nihao", "hi", "hadoop", "kafka", "scala", "hi"))
// 按照首字母第一个单词相同分组
val newRDD: RDD[(String, Ite