小白学spark日记day1——RDD和算子

RDD:

弹性分布式数据集

RDD的创建(RDD不存数据只是一个抽象的描述)
      - 通过集合创建RDD
          - parallelize
          - makeRDD
      - 通过文件创建RDD
          - textFile

总结(RDD的五⼤特征):
1.RDD可以看做是⼀些列partition所组成的
2.RDD之间的依赖关系
3.算⼦是作⽤在partition之上的
4.分区器是作⽤在kv形式的RDD上
5.partition提供的最佳计算位置,利于数据处理的本地化即计算向数据移动⽽不是移动数据
注意 : RDD本身是不存储数据,可以看做RDD本身是⼀个引⽤数据

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CreateRDD {
    // 1. 必备: 初始化SparkContext对象
    private val sc:SparkContext=new SparkContext(new SparkConf().setAppName("RDD-Create").setMaster("local"))

    def main(args: Array[String]): Unit = {
        createRddFronCollection()
    }
    /**
     * 1. 通过集合创建RDD
     */
    def createRddFronCollection():Unit={
        // 1. 通过parallelize创建RDD
        //parallelize()函数的第二个参数表示分区,默认是1,此处为2,表示将列表对应的RDD对象分为两个区
        val rdd1:RDD[Int] = sc.parallelize(Array(1,2,3,4))
        val rdd4:RDD[Int] = sc.parallelize(Array(1,2,3,4),2)
        // 2. 通过makeRDD创建RDD对象,底层调用还是parallelize
        val rdd2:RDD[String]=sc.makeRDD(Array("林黛玉", "贾宝玉", "薛宝钗", "袭人"))
        println(rdd2)
        // 3. "通过Map"创建RDD
        val res: List[(String, String)] = Map("name" -> "小白", "age" -> "18", "gender" -> "male").toList
        val rdd3:RDD[(String, String)] = sc.parallelize(res)

        /**
         * 2. 通过文件创建RDD
         */
        def createRddFromFile(): Unit = {
            val rdd: RDD[String] = sc.textFile("file/spark/wc/input/words")
        }
    }
}

RDD算子是RDD中用于对RDD的每⼀个Partition的数据进⾏操作转换执行,用于数据读取处理,是SparkCore中的核心,RDD算⼦可分为两类算子,转换算子和执行算子,转换算子用于数据转换处理,执行算子用于数据结果输出。

简单算子:

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.junit.Test

/**
 * RDD的简单算子演示:
 * map、mapPartitions、mapPartitionsWithIndex
 * flatMap
 * glom
 * mapValues
 * filter
 * keyBy
 * groupBy
 * reduceByKey
 * foldByKey
 */
class SimpleRDD {
    // 1. 准备工作
    val sc: SparkContext = new SparkContext(new SparkConf().setMaster("local").setAppName("Simple RDD"))

    /**
     * 转换算子: map
     * 逻辑: 对RDD中的每一个元素进行映射,映射为指定的值
     * 对每一个分区中的每一个数据进行映射
     */
    @Test def mapTest(): Unit = {
        // 1. 实例化一个集合
        val array: Array[String] = Array("dog", "cat", "elephent", "lion", "tiger", "monkey", "panda")
        // 2. 通过集合,创建RDD
        val rdd1: RDD[String] = sc.parallelize(array)
        // 3. 映射元素(需求:元素都替换成自己的长度)
        val rdd2: RDD[Int] = rdd1.map(_.length)
        // 4. 输出rdd2中描述的数据
        rdd2.foreach(println)

        // 5. 映射元素(需求:元素都替换成(元素, 长度))
        val rdd3: RDD[(String, Int)] = rdd1.map(word => (word, word.length))
        rdd3.foreach(println)
    }

    /**
     * 转换算子: mapPartitions
     * 逻辑: 也是一个映射,类似于map,但是与map是不一样
     * 和map的区别:
     *     - map: 作用在每一个分区的每一个元素上的
     *     - mapPartitions: 作用在一个分区上的
     *
     * mapPartitions: 会将一个分区的数据,作为一个整体,来进行整体的映射
     * mapPartitions比 map要常用
     */
    @Test def mapPartitionsTest(): Unit = {
        // 1. 实例化一个集合
        val array: Array[String] = Array("宋江", "卢俊义", "吴用", "公孙胜", "关胜")
        // 2. 创建一个RDD
        val rdd: RDD[String] = sc.parallelize(array, 2)
        rdd.foreach(println)
        // 3. 元素映射(参数是一个迭代器 iterator,返回值要求也是一个迭代器 iterator)
        val rdd1: RDD[Int] = rdd.mapPartitions(iterator => iterator.map(_.length))

        rdd1.foreach(println)
    }

    /**
     * 转换算子: mapPartitionsWithIndex
     * 逻辑: 等同于mapPartitions,也是对一个分区的映射
     *      对比mapPartitions,多出一个分区的下标
     */
    @Test def mapPartitionsWithIndexTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd: RDD[String] = sc.parallelize(Array("金莲小姐姐", "婆惜小姐姐", "三娘小姐姐", "师师小姐姐"), 3)
        rdd.foreach(println)
        // 2. 需求: 将原来的元素映射成 分区号+名字+名字的长度
        val rdd1: RDD[(Int, String, Int)] = rdd.mapPartitionsWithIndex((index, iterator) => iterator.map(str => (index, str, str.length)))
        rdd1.foreach(println)
    }

    /**
     * 转换算子: flatMap
     * 逻辑: 扁平化映射,针对RDD中存储的元素是集合的情况,将所有的集合中的元素提取到一个RDD中
     */
    @Test def flatMapTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[Array[String]] = sc.parallelize(Array(Array("赵云", "关羽", "张飞", "黄忠", "马超"), Array("张辽", "许褚", "典韦"), Array("陆逊", "张昭", "周瑜")))
        val rdd2: RDD[String] = rdd1.flatMap(_.iterator)  // _.toList
        rdd2.foreach(println)

        // 2. 通过集合,创建RDD
        val rdd3: RDD[String] = sc.parallelize(Array("赵云  关羽  张飞", "诸葛亮  司马懿  周瑜", "黄盖  陆逊"))
        val rdd4: RDD[String] = rdd3.flatMap(_.split(" +"))
        rdd4.foreach(println)

        // 3.
        val rdd5: RDD[Array[String]] = sc.parallelize(Array(Array("赵云  关羽   张飞", "诸葛亮 司马懿 周瑜"), Array("孙策  大乔", "周瑜  小乔")))
        // val rdd6: RDD[String] = rdd5.flatMap(array => array.flatMap(_.split(" +")))
        val rdd6: RDD[String] = rdd5.flatMap(_.flatMap(_.split(" +")))
        rdd6.foreach(println)


        // Array("a b c ", "d e f")  =>  Array ("a", "b", "c", "d", "e", "f")
    }

    /**
     * 转换算子: glom
     * 逻辑: 将一个分区中的所有的元素,聚合成一个数组,放回到之前的分区中
     *      不会修改分区的数量
     */
    @Test def glomTest(): Unit = {
        // 1. 通过集合,创建RDD(将1到20的数字,分到了4个分区中)
        val rdd1: RDD[Int] = sc.parallelize(1 to 20, 4)
        rdd1.foreach(println)
        // 2. 将每一个分区的元素做成一个数组
        val rdd2: RDD[Array[Int]] = rdd1.glom()

        rdd2.foreach(array => println(array.mkString(", ")))

        // 输出RDD中的分区数量
        println(rdd2.getNumPartitions)
    }

    /**
     * 转换算子: mapValues
     * 注意: 只针对PariedRDD,也就是说RDD描述的数据是若干个键值对
     *      (其实,这里可以操作的数据,可以可以是RDD(Tuple2))
     * 逻辑: 对键值对的值做映射,不对键做任何处理
     */
    @Test def mapValues(): Unit = {
        // 1. 通过集合创建RDD
        val rdd1: RDD[String] = sc.parallelize(Array("贾宝玉", "林黛玉", "薛宝钗", "探春", "迎春", "惜春"))
        // 2. 对元素做映射,以名字的长度作为键,以名字作为值
        val rdd2: RDD[(Int, String)] = rdd1.map(n => (n.length, n))
        // 3. 让每一个人的名字后面添加一个叹号
        val rdd3: RDD[(Int, String)] = rdd2.mapValues(_ + "!")
        rdd3.foreach(println)

        //
        val array1: Array[String] = Array("贾宝玉", "林黛玉", "薛宝钗", "探春")
        val array2: Array[Int] = Array(18, 19, 17, 16)
        val pairs: Array[(String, Int)] = array1.zip(array2)
        val rdd4: RDD[(String, Int)] = sc.parallelize(pairs)
        // 需求: 让每一个人的年龄增20岁!
        val rdd5: RDD[(String, Int)] = rdd4.mapValues(_ + 20)
        rdd5.foreach(println)
    }

    /**
     * 转换算子: filter
     * 逻辑: 保留RDD中满足条件的数据
     */
    @Test def filterTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[(String, Int)] = sc.parallelize(List(("贾宝玉", 18), ("林黛玉", 17), ("薛宝钗", 18), ("探春", 16)))
        // 2. 保留所有的成年的数据
        val rdd2: RDD[(String, Int)] = rdd1.filter(_._2 >= 18)

        rdd2.foreach(println)
    }

    /**
     * 转换算子: keyBy
     * 逻辑: 将RDD中的数据,以指定的逻辑得到的结果作为键,数据本身作为值
     */
    @Test def keyByTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[String] = sc.parallelize(Array("张角", "张宝", "董卓", "貂蝉", "诸葛亮", "司马懿", "关云长", "张翼德", "诸葛武侯"))
        // 2. 给这些元素找键
        val rdd2: RDD[(Int, String)] = rdd1.keyBy(_.length)

        rdd2.foreach(println)
    }

    /**
     * 转换算子: groupBy
     * 逻辑: 对RDD中的元素,按照指定的逻辑得到的结果,进行分组
     */
    @Test def groupByTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[String] = sc.parallelize(Array("张角", "张宝", "董卓", "貂蝉", "诸葛亮", "司马懿", "关云长", "张翼德", "诸葛武侯"))
        // 2. 将所有的相同的长度的名字视为一个分组
        val rdd2: RDD[(Int, Iterable[String])] = rdd1.groupBy(_.length)

        // val rdd3: RDD[(Int, List[String])] = rdd2.mapValues(_.toList)

        // 3. 遍历输出结果
        rdd2.foreach(tuple => println(s"${tuple._1} => ${tuple._2}"))
    }

    /**
     * 转换算子: reduceByKey
     * 逻辑: 针对PariedRDD,按照键进行分组,将所有的值进行运算
     */
    @Test def reduceByKeyTest(): Unit = {
        // 1. 通过集合,创建一个RDD
        val rdd1: RDD[String] = sc.parallelize(Array("Tom  Jerry  Tom  Jerry", "Tom  Jerry  Tom  Jerry", "Hank  Hank  Tom  Jerry"))
        // 2. 计算wordcount
        val rdd2: RDD[String] = rdd1.flatMap(_.split(" +"))
        // 3. 以单词为键,1作为值,构成一个PairedRDD
        val rdd3: RDD[(String, Int)] = rdd2.map(n => (n, 1))
        // 4. 将相同的键视为一个分组,将值进行累加
        val rdd4: RDD[(String, Int)] = rdd3.reduceByKey(_ + _)

        rdd4.foreach(println)
    }

    /**
     * 转换算子: foldByKey
     * 逻辑: 和reduceByKey差不多,在每次进行聚合运算的时候,都会添加上一个默认的值
     * 尽量少用,集群模式下可能会出现多个标签
     */
    @Test def foldByKeyTest(): Unit = {
        // 1. 通过集合,准备RDD
        val rdd1: RDD[String] = sc.parallelize(Array("三国演义", "水浒传", "红楼梦", "西游记", "诛仙", "神墓", "斗罗大陆", "斗破苍穹", "武动乾坤", "大主宰", "遮天"))
        // 2. 添加键
        // rdd1.map(n => (n.length, n))
        val rdd2: RDD[(Int, String)] = rdd1.keyBy(_.length)

        val rdd3: RDD[(Int, String)] = rdd2.reduceByKey(_ + "," + _)
        rdd3.foreach(println)

        // 3.
        val rdd4: RDD[(Int, String)] = rdd2.foldByKey("书名: ")(_ + ", " + _)
        rdd4.foreach(println)
    }
}

转换算子:

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.junit.Test

/**
 * aggregateByKey
 * combineByKey
 * sortByKey
 * sortBy
 * union, ++
 * intersection
 * distinct
 * join
 * repartition, coalease
 * cogroup
 * sample
 *
 */
class ComplicatedRDD {
    val sc: SparkContext = new SparkContext(new SparkConf().setMaster("local").setAppName("complicated-RDD"))

    /**
     * 转换算子: aggregateByKey
     * 注意: 作用在PairedRDD身上
     * 逻辑: 也是一个聚合运算,类似于reduceByKey和foldByKey
     *       aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U)
     *       - 按照相同的Key进行分组,将所有的value放入到一个分组中,对其进行处理
     *       - 依次将每一个分区中的相同的键对应的数据进行运算,按照seqOp进行运算,得到结果。这个过程中,会使用到zeroValue
     *       - 每一个分区都计算完成后,将不同的分区中相同的键聚合到一起,这个过程使用的是combOp函数进行聚合。这个过程中,不会使用到zeroValue
     *       - 相同的key对应的value聚合
     * zeroValue: 分区内计算的初始值
     * seqOp: 分区内聚合的计算逻辑
     * combOp: 不同分区之间进行聚合的计算逻辑
     *
     *
     */
    @Test def aggregateByKeyTest(): Unit = {
        // 1. 准备数据
        val rdd: RDD[(String, Int)] = sc.parallelize(Array(("贝吉塔", 3), ("卡卡罗特", 5) ,("贝吉塔", 4), ("樱木花道", 6), ("卡卡罗特", 7), ("贝吉塔", 8)), 2)
        rdd.foreach(println)
        // 2. 聚合
        val res: RDD[(String, Int)] = rdd.aggregateByKey(5)(Math.max, _ +_)

        res.foreach(println)
    }

    /**
     * 转换算子: combineByKey
     * 注意: 作用在PairedRDD身上
     * 逻辑:
     *       combineByKey[C](createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C)
     *
     *       - createCombiner:
     *         combineByKey在进行逻辑处理的时候,会依次遍历分区中的每一个键值对
     *         如果这个key是第一次遍历到的,此时就会触发这个函数,为其创建一个自定义的累加器的初始值
     *
     *       - mergeValue:
     *         如果一个分区中所有的数据,键在之前都已经遍历过了,已经创建好了累加器了,此时就会触发这个方法,进行累加(分区内)
     *
     *       - mergeCombiners:
     *         不同的分区中,可能存在相同的键,当所有的分区都聚合完成,最后将不同的分区中的数据进行合并,相同的键按照这个函数进行合并
     */
    @Test def combineByKeyTest(): Unit = {
        // 1. 准备数据
        val rdd: RDD[(String, Int)] = sc.parallelize(Array(("chinese", 90), ("math", 97), ("math", 90), ("english", 87), ("math", 88), ("chinese", 98)), 2)
        // 2. 统计每一个学科的总成绩和成绩的数量
        val rdd2: RDD[(String, (Int, Int))] = rdd.combineByKey(
            //第一次调用这个函数
            v => (v, 1),
            //第二次以及后续调用这个函数
            (combiner: (Int, Int), v) => (combiner._1 + v, combiner._2 + 1),
            //各分区再合并
            (c1: (Int, Int), c2: (Int, Int)) => (c1._1 + c2._1, c1._2 + c2._2)
        )
        // 3. 遍历最后的结果
        rdd2.foreach(println)
    }


    /**
     * 转换算子: sortByKey
     * 注意事项: 作用在PairedRDD身上的
     * 逻辑: 将数据按照key进行排序
     *
     *     - ascending: true => 升序排列, false => 降序排列
     *     - numPartitions: 排序之后的数据放到几个分区中,默认是照原来的分区数量
     */
    @Test def sortByKeyTest(): Unit = {
        // 2. 准备数据
        val rdd: RDD[(Int, String)] = sc.parallelize(Array("jerry", "tomtomtom", "hank", "jaifei", "snoopy"), 3).keyBy(_.length)
        val res1: RDD[(Int, String)] = rdd.sortByKey(ascending = false, 1)
        res1.foreach(println)
    }

    /**
     * 转换算子: sortBy
     */
    @Test def sortByTest(): Unit = {
        //
        val rdd: RDD[String] = sc.parallelize(Array("tom", "jerry", "snoppy", "hank", "kitty"))
        // 按照字符串的长度进行排序
        val rdd2: RDD[String] = rdd.sortBy(_.length, ascending = true, 1)

        rdd2.foreach(println)


        val rdd3: RDD[(String, Int)] = rdd.map(s => (s, s.length))
        val res: RDD[(String, Int)] = rdd3.sortBy(_._2)
        res.foreach(println)
    }

    /**
     * 转换算子: union
     * 逻辑: 对两个RDD进行合并,合并结果不去重
     * 合并之后,会生成参与合并的两个RDD的分区数量总和的分区数
     */
    @Test def unionTest(): Unit = {
        val rdd1: RDD[Int] = sc.parallelize(Array(1, 2, 3, 3, 4, 5, 5), 2)
        val rdd2: RDD[Int] = sc.parallelize(Array(2, 3, 3, 4, 4, 5, 6), 3)
        // 合并
        val res: RDD[Int] = rdd1.union(rdd2)

        res.foreach(println)
        println(res.getNumPartitions)

        val res1: RDD[Int] = rdd1.++(rdd2)
        res1.foreach(println)
        println(res1.getNumPartitions)
    }

    /**
     * 转换算子: distinct
     * 逻辑: 去除重复元素
     */
    @Test def distinctTest(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(Array(1, 2, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6))
        val res: RDD[Int] = rdd.distinct()
        res.foreach(println)

        val res1: RDD[Int] = rdd.distinct(2)
        println(res1.getNumPartitions)
    }

    /**
     * 转换算子: intersection
     * 计算两个RDD的交集
     */
    @Test def intersectionTest(): Unit = {
        val rdd1: RDD[Int] = sc.parallelize(Array(1, 2, 3, 3, 4, 5, 6))
        val rdd2: RDD[Int] = sc.parallelize(Array(2, 3, 3, 4, 7, 8, 9))

        // 计算交集
        val rdd3: RDD[Int] = rdd1.intersection(rdd2)

        rdd3.foreach(println)
    }

    /**
     * 转换算子: join
     * 注意事项: 作用在PairedRDD身上的
     * 逻辑: 类似于SQL中的连接查询,将两个RDD中的数据连接到一起,这会形成笛卡尔积
     */
    @Test def joinTest(): Unit = {
        // 1. 准备数据
        val rdd1: RDD[(Int, String)] = sc.parallelize(Array("Tom", "Jerry", "诸葛亮亮", "上官婉儿")).keyBy(_.length)
        val rdd2: RDD[(Int, String)] = sc.parallelize(Array("chinese", "math", "ABCD")).keyBy(_.length)

        // 2. (内)连接
        val res0: RDD[(Int, (String, String))] = rdd1.join(rdd2)
        res0.foreach(println)

        // 3. 左连接
        val res1: RDD[(Int, (String, Option[String]))] = rdd1.leftOuterJoin(rdd2)
        res1.foreach(println)

        // 4. 右连接
        val res2: RDD[(Int, (Option[String], String))] = rdd1.rightOuterJoin(rdd2)
        res2.foreach(println)

        // 5. 全连接
        val res3: RDD[(Int, (Option[String], Option[String]))] = rdd1.fullOuterJoin(rdd2)
        res3.foreach(println)
    }

    /**
     * 转换算子: repartition
     * 对一个RDD进行重新的分区(repartition适合扩大分区)
     */
    @Test def repartitionTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[Int] = sc.parallelize(1 to 20, 2)
        rdd1.mapPartitionsWithIndex((index, iterator) => iterator.map(index + " => " + _)).foreach(println)

        // 2. 重新分区
        val rdd2: RDD[Int] = rdd1.repartition(4)
        rdd2.mapPartitionsWithIndex((index, iterator) => iterator.map(index + " => " + _)).foreach(println)
    }

    /**
     * 转换算子: coalesce
     * 对一个RDD进行重新的分区
     *
     * repartition底层就是调用的coalesce来实现的重新分区
     * - 如果要扩大分区,则一定要触发shuffle,否则将无法分区
     * - 如果要缩小分区,可以触发shuffle,也可以不触发
     *
     * repartition:
     *     - 由于底层调用coalesce强制触发shuffle了,因此适合于扩大分区
     * coalesce:
     *     - 可以自己决定是否要触发shuffle,默认是不触发的,因此适合缩小分区
     *
     */
    @Test def coalesceTest(): Unit = {
        // 1. 通过集合,创建RDD
        val rdd1: RDD[Int] = sc.parallelize(1 to 20, 4)
        rdd1.mapPartitionsWithIndex((index, iterator) => iterator.map(index + " => " + _)).foreach(println)

        // 2. 重新分区
        val rdd2: RDD[Int] = rdd1.coalesce(2, true)
        rdd2.mapPartitionsWithIndex((index, iterator) => iterator.map(index + " => " + _)).foreach(println)
    }

    /**
     * 转换算子: cogroup
     * 注意事项: 作用在PairedRDD身上的算子
     * 逻辑: 将多个RDD中的数据,按照Key进行分组,将相同Key的值聚合成一个集合
     */
    @Test def cogroupTest(): Unit = {
        val rdd1: RDD[(String, Int)] = sc.parallelize(Array(("chinese", 90), ("chinese", 87), ("chinese", 100), ("math", 99), ("math", 80)))
        val rdd2: RDD[(String, Int)] = sc.parallelize(Array(("chinese", 89), ("chinese", 76), ("math", 100), ("math", 76), ("math", 57)))

        // 一个RDD和另外的一个RDD进行聚合分组
        val res1: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
        res1.foreach(println)

        val res2: RDD[(String, (Iterable[Int], Iterable[Int], Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2, rdd2, rdd2)
        res2.foreach(println)
    }

    /**
     * 转换算子: sample
     *sample算子是用来抽样用的,其有3个参数
     * withReplacement:表示抽出样本后是否在放回去,true表示会放回去,这也就意味着抽出的样本可能有重复
     * fraction :抽出多少,这是一个double类型的参数,0-1之间,eg:0.3表示抽出30%
     */
    @Test def sampleTest(): Unit = {
        // 1. 准备一个RDD
        val rdd1: RDD[Int] = sc.parallelize(1 to 1000)

        // 2.
        val res: RDD[Int] = rdd1.sample(withReplacement = false, 0.1)

        res.foreach(println)
    }
}

行动算子:

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.junit.Test

/**
 * reduce
 * fold
 * aggregate
 * collect
 * collectAsMap
 * count
 * countByKey
 * take
 * takeSample
 * takeOrdered
 * top
 * first
 * foreach
 * saveAsTextFile
 */
class ActionRDD {
    private val sc: SparkContext = new SparkContext(new SparkConf().setMaster("local").setAppName("Action-RDD"))

    /**
     * 行动算子: reduce
     * 逻辑: 将RDD中的数据,按照指定的规则进行聚合运算
     */
    @Test def reduceTest(): Unit = {
        // 1. 通过集合,创建一个RDD
        val rdd: RDD[Int] = sc.parallelize(1 to 100, 2)

        // 2. 聚合运算
        val res: Int = rdd.reduce(_ + _)

        // 3. 输出结果
        println(res)
    }

    /**
     * 行动算子: fold
     * 聚合运算,在分区内进行聚合的时候,以及分区之间进行聚合的时候都会带上zeroValue
     */
    @Test def foldTest(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(1 to 100, 2)
        rdd.foreach(println)
        /*
        * 第一步: 先对分区内元素做op操作, zeroValue作为初始值
        * 1+..+50+10=1285
        * 51+...+100=10=3785
        * 第二步: 再对多个分区做op操作, zeroValue也作为初始值
        *1285+3785+10=5080
        */
        val res: Int = rdd.fold(10)(_ + _)
        println(res)
    }

    /**
     * 行动算子: aggregate
     *
     * 定制分区内的计算逻辑和分区之间的计算逻辑
     * 分区的计算逻辑,需要考虑到zeroValue
     * 分区之间的计算逻辑,也需要考虑到zeroValue
     *
     */
    @Test def aggregateTest(): Unit = {
        // 1. 准备一个集合
        val rdd1: RDD[Int] = sc.parallelize(1 to 100, 2)
        // 2. 聚合
        val res: Int = rdd1.aggregate(8)(Math.max, _ - _)
        /*
        第一个分区最大值80
        第二个分区最大值100
        最后结果:80+100+80(zeroValue)
         */
        println(res)
    }

    /**
     * 行动算子: collect
     * 将RDD中的数据都搜集起来,返回给Driver端
     */
    @Test def collectTest(): Unit = {
        val rdd: RDD[Range.Inclusive] = sc.parallelize(Array(1 to 100))
        val res: Array[Range.Inclusive] = rdd.collect()
        res.foreach(println)
    }

    /**
     * 行动算子: collectAsMap
     * 注意事项: 作用在PairedRDD身上的
     * 将PairedRDD的所有数据搜集起来,存入一个Map,返回到Driver端
     */
    @Test def collectAsMap(): Unit = {
        val rdd: RDD[(Int, String)] = sc.parallelize(Array("Lily", "Uncle Wang", "Polly")).keyBy(_.length)
        val res: collection.Map[Int, String] = rdd.collectAsMap()
        res.foreach(println)
    }

    /**
     * 行动算子: count
     * 统计RDD描述的数据中有多少个
     */
    @Test def countTest(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(1 to 100)

        val count: Long = rdd.count()
        println(count)
    }

    /**
     * 行动算子: countByKey
     * 注意事项: 作用在PariedRDD
     * 统计一个PairedRDD中,每一个键有多少个对应的值(键出现了多少次)
     */
    @Test def countByKeyTest(): Unit = {
        val rdd: RDD[(Int, String)] = sc.parallelize(Array("Jim", "Tom", "Lily", "Lucy", "Polly", "Snoppy")).keyBy(_.length)
        rdd.foreach(println)
        val res: collection.Map[Int, Long] = rdd.countByKey()
        println(res)
    }

    @Test def wordcount(): Unit = {
        val rdd: RDD[String] = sc.parallelize(Array("hello world hello hi hello", "java  hadoop java hadoop spark spark scala", "scala flume flume sqoop sqoop azkaban"))
        val res: collection.Map[String, Long] = rdd.flatMap(_.split(" +"))
            .map((_, 1))
            .countByKey()
        println(res)
    }

    /**
     * 行动算子: take
     * 逻辑: 获取RDD中前N个元素,获取由这些元素组成的集合
     */
    @Test def takeTest(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(Array(1, 2, 3, 4, 5, 6, 7))
        val res: Array[Int] = rdd.take(3)
        res.foreach(println)
    }

    /**
     * 行动算子: takeSample
     */
    @Test def takeSample(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(1 to 100)
        val res: Array[Int] = rdd.takeSample(withReplacement = true, 10)
        res.foreach(println)
    }

    /**
     * 行动算子: takeOrdered
     * 逻辑: 从RDD中获取最小的几个元素
     */
    @Test def takeOrdered(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(Array(1, 3, 5, 7, 9, 0, 8, 6, 4, 2))
        val res: Array[Int] = rdd.takeOrdered(5)
        res.foreach(println)
    }

    /**
     * 行动算子: top
     * 逻辑: 从RDD中获取最大的几个元素
     */
    @Test def top(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(Array(1, 3, 5, 7, 9, 0, 8, 6, 4, 2))
        val res: Array[Int] = rdd.top(5)
        res.foreach(println)
    }

    /**
     * 行动算子: first
     * 逻辑: 获取RDD中第一个元素,相当于 take(1)
     */
    @Test def first(): Unit = {
        val res: Int = sc.parallelize(Array(1, 3, 5, 7, 9, 0, 8, 6, 4, 2)).first()
        println(res)
    }
    /**
     * 保存
     */
    @Test def saveTest(): Unit = {
        val rdd: RDD[Int] = sc.parallelize(1 to 100, 4)
        rdd.saveAsTextFile("file/spark/out")
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值