spark记录(3)spark算子之Transformation

一、map、flatMap、mapParations、mapPartitionsWithIndex

1.1 map

map十分容易理解,他是将源JavaRDD的一个一个元素的传入call方法,并经过算法后一个一个的返回从而生成一个新的JavaRDD。

(1) 使用Java进行编写

public static void map() {
        List<String> list = Arrays.asList("李光洙","刘在石","哈哈","宋智孝");
        JavaRDD<String> rdd = jsc.parallelize(list);
        
        JavaRDD<String> map = rdd.map(new Function<String, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public String call(String name) throws Exception {
                return "hello,"+name;
            }
        });
        map.foreach(new VoidFunction<String>() {
            
            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            @Override
            public void call(String msg) throws Exception {
                System.out.println(msg);
            }
        });
        
    }

(2) 使用scala进行编写

  def map(): Unit = {
    val list = List("李光洙","刘在石","哈哈","宋智孝");
    val rdd = sc.parallelize(list)
    val map = rdd.map(s => "hello," + s).foreach(println) 
  }

(3)运行结果

(4) 总结

可以看出,对于map算子,源JavaRDD的每个元素都会进行计算,由于是依次进行传参,所以他是有序的,新RDD的元素顺序与源RDD是相同的。而由有序又引出接下来的flatMap。

1.2 flatMap

flatMap与map一样,是将RDD中的元素依次的传入call方法,他比map多的功能是能在任何一个传入call方法的元素后面添加任意多元素,而能达到这一点,正是因为其进行传参是依次进行的。

(1) 使用Java进行编写

    public static void flatmap() {
        List<String> list = Arrays.asList("李光洙 刘在石","哈哈 宋智孝");
        JavaRDD<String> rdd = jsc.parallelize(list);
        
        JavaRDD<String> map = rdd.flatMap(new FlatMapFunction<String, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterable<String> call(String line) throws Exception {
                return Arrays.asList(line.split(" "));
            }
        }).map(new Function<String, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public String call(String s) throws Exception {
                return "你好," + s;
            }
        });
        
        map.foreach(new VoidFunction<String>() {
            
            @Override
            public void call(String s) throws Exception {
                System.out.println(s);
            }
        });
        
    }

(2) 使用scala进行编写

  def flatmap(): Unit = {
    val list = List("李光洙 刘在石","哈哈 宋智孝");
    val rdd = sc.parallelize(list)
    rdd.flatMap(_.split(" ")).map("你好,"+_).foreach(println)
  }

(3) 运行结果

(4) 总结

flatMap的特性决定了这个算子在对需要随时增加元素的时候十分好用,比如在对源RDD查漏补缺时。

map和flatMap都是依次进行参数传递的,但有时候需要RDD中的两个元素进行相应操作时(例如:算存款所得时,下一个月所得的利息是要原本金加上上一个月所得的本金的),这两个算子便无法达到目的了,这是便需要mapPartitions算子,他传参的方式是将整个RDD传入,然后将一个迭代器传出生成一个新的RDD,由于整个RDD都传入了,所以便能完成前面说的业务。

map是对RDD中元素逐一进行函数操作映射为另外一个RDD,而flatMap操作是将函数应用于RDD之中的每一个元素,将返回的迭代器的所有内容构成新的RDD。而flatMap操作是将函数应用于RDD中每一个元素,将返回的迭代器的所有内容构成RDD。

flatMap与map区别在于map为“映射”,而flatMap“先映射,后扁平化”,map对每一次(func)都产生一个元素,返回一个对象,而flatMap多一步就是将所有对象合并为一个对象。

1.3 mapPartitions

与map方法类似,map是对rdd中的每一个元素进行操作,而mapPartitions(foreachPartition)则是对rdd中的每个分区的迭代器进行操作。如果在map过程中需要频繁创建额外的对象(例如将rdd中的数据通过jdbc写入数据库,map需要为每个元素创建一个链接而mapPartition为每个partition创建一个链接),则mapPartitions效率比map高的多。

(1) 使用Java进行编写

    public static void mapPartitions() {
        JavaRDD<String> textFile = jsc.textFile("words",3);
        textFile.mapPartitions(new FlatMapFunction<Iterator<String>, String>() {
            
            private static final long serialVersionUID = 1L;

            @Override
            public Iterable<String> call(Iterator<String> is) throws Exception {
                System.out.println("创建数据库连接。。。。");
                List<String> list = new ArrayList<String>();
                while(is.hasNext()) {
                    list.add(is.next());
                    System.out.println("模拟向数据库插入批量数据。。。");
                }
                System.out.println("关闭数据库连接。。。");
                return list;
            }
        }).collect();
        
    }

(2) 使用scala进行编写

  def mapPartitions: Unit = {
    val rdd1 = sc.textFile("words")
    val mapResult = rdd1.mapPartitions(iter =>{
        println("打开数据库。。。")
        val list = List()
        while(iter.hasNext){
          list.addString(new StringBuilder(iter.next()))
          println("插入数据库。。。")
        }
        println("关闭数据库。。。")
        list.iterator
      }, false)
    mapResult.foreach(println)
  }

(3) 运行结果

(4)总结

mapPartitions比较适合需要分批处理数据的情况,比如将数据插入某个表,每批数据只需要开启一次数据库连接,大大减少了连接开支。

1.4 mapPartitionsWithIndex

每次获取和处理的就是一个分区的数据,并且知道处理的分区的分区号是什么

(1)使用Java编写

    public static void mapPartitionsWithIndex() {
        List<String> list = Arrays.asList("李光洙","刘在石","哈哈","宋智孝");
        JavaRDD<String> rdd = jsc.parallelize(list,3);
        
        JavaRDD<String> rdd2 = rdd.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterator<String> call(Integer index, Iterator<String> iter) throws Exception {
                List<String> list =  new ArrayList<String>();
                while(iter.hasNext()) {
                    list.add(index+"_"+iter.next());
                }
                return list.iterator();
            }
        }, true);
        
        rdd2.foreach(new VoidFunction<String>() {
            
            @Override
            public void call(String s) throws Exception {
                System.out.println(s);
            }
        });
        
    }

(2)使用scala编写

def mapPartitionsWithIndex: Unit = {
    val list = List("李光洙","刘在石","哈哈","宋智孝")
    val rdd1 = sc.parallelize(list, 3) 
    val rdd2 = rdd1.mapPartitionsWithIndex((index,iter)=>{
      val l = ListBuffer[String]()
      while(iter.hasNext){
        val v = iter.next()
        l.append(index+"_"+v)
      }
      l.iterator
    }, true).foreach(println)
  }

(3)结果

 二.

2.1 fillter

 过滤操作,满足filter内function函数为true的RDD内所有元素组成一个新的数据集。如:filter(a == 1)。

(1)使用Java编写

    public static void fillter() {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
        JavaRDD<Integer> rdd = jsc.parallelize(list);
        rdd.filter(new Function<Integer, Boolean>() {
            
            private static final long serialVersionUID = 1L;

            @Override
            public Boolean call(Integer i) throws Exception {
                return i%2==0;
            }
        }).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer a) throws Exception {
                System.out.println(a);
            }
        });
    }

(2)使用scala编写

  def fillter: Unit = {
    val list = List(1,2,3,4,5,6)
    val rdd1 = sc.parallelize(list)
    rdd1.filter(_%2==0).foreach(println)
  }

(3)结果

2.2 sample(withReplacement, fraction, seed)

采样操作,用于从样本中取出部分数据。withReplacement是否放回,fraction采样比例,seed用于指定的随机数生成器的种子。(是否放回抽样分true和false,fraction取样比例为(0, 1]。seed种子为整型实数。)

(1)使用Java编写

    public static void sample() {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
        JavaRDD<Integer> rdd = jsc.parallelize(list);
        rdd.sample(false, 0.5, 1).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
        
    }

(2)使用scala编写

  def sample: Unit = {
    val list = List(1,2,3,4,5,6)
    val rdd1 = sc.parallelize(list)
    rdd1.sample(false,0.5, 1).foreach(println) 
  }

(3)结果

 

2.3 cartesian

cartesian是用于求笛卡尔积的,该操作不会执行shuffle操作。

(1)使用Java编写

    public static void cartesian() {
        List<String> list1 = Arrays.asList("A","B","C");
        List<Integer> list2 = Arrays.asList(1,2,3);
        
        JavaRDD<String> rdd1 = jsc.parallelize(list1);
        JavaRDD<Integer> rdd2 = jsc.parallelize(list2);
        
        rdd1.cartesian(rdd2).foreach(new VoidFunction<Tuple2<String,Integer>>() {
            
            @Override
            public void call(Tuple2<String, Integer> tuple) throws Exception {
                System.out.println(tuple._1+"---->"+tuple._2);
            }
        });
    }

(2)使用scala编写

  def cartesian: Unit = {
    val list1 = List("A","B","C")
    val list2 = List(1,2,3)
    
    val rdd1 = sc.parallelize(list1)
    val rdd2 = sc.parallelize(list2)
    rdd1.cartesian(rdd2).foreach(tuple =>println(tuple._1+"--->"+tuple._2))
  }

(3)结果

 

2.4 reduceByKey(function,[numTasks])

reduceByKey仅将RDD中所有K,V对中K值相同的V进行合并。

(1)使用Java编写

    public static void reduceByKey() {
        JavaRDD<String> rdd = jsc.textFile("words");
        rdd.flatMap(new FlatMapFunction<String, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterable<String> call(String s) throws Exception {
                return Arrays.asList(s.split(" "));
            }
        }).mapToPair(new PairFunction<String, String, Integer>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<String, Integer>(s, 1);
            }
        }).reduceByKey(new Function2<Integer, Integer, Integer>() {
            
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                // TODO Auto-generated method stub
                return v1+v2;
            }
        }).foreach(new VoidFunction<Tuple2<String,Integer>>() {
            
            @Override
            public void call(Tuple2<String, Integer> s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)使用scala编写

  def reduceByKey: Unit = {
    sc.textFile("words").flatMap(_.split(" ")).map(new Tuple2(_,1)).reduceByKey((_+_)).foreach(println)
  }

(3)结果

三、union,join和groupByKey 

3.1 union

当要将两个RDD合并时,便要用到union和join,其中union只是简单的将两个RDD累加起来,可以看做List的addAll方法。就想List中一样,当使用union及join时,必须保证两个RDD的泛型是一致的。不去重。

(1)使用Java编写

    public static void union() {
        List<Integer> list1 = Arrays.asList(1,2,3,4);
        List<Integer> list2 = Arrays.asList(3,4,5,6);
        JavaRDD<Integer> rdd1 = jsc.parallelize(list1);
        JavaRDD<Integer> rdd2 = jsc.parallelize(list2);
        rdd1.union(rdd2).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer i) throws Exception {
                System.out.println(i);
            }
        });
    }

(2)使用scala编写

  def union: Unit = {
    val list1 = List(1,2,3,4)
    val list2 = List(3,4,5,6)
    val rdd1 = sc.parallelize(list1)
    val rdd2 = sc.parallelize(list2)
    rdd1.union(rdd2).foreach(println)
  }

(3)结果

3.2 groupByKey

groupBy是将RDD中的元素进行分组,组名是call方法中的返回值,而顾名思义groupByKey是将PairRDD中拥有相同key值得元素归为一组。

(1)使用Java编写

    public static void groupByKey() {
        List<Tuple2<String,String>> list = Arrays.asList(
                    new Tuple2("Doctor","A"),
                    new Tuple2("Actor","B"),
                    new Tuple2("Doctor","C"),
                    new Tuple2("Actor","D")
                );
        JavaPairRDD<String, String> rdd = jsc.parallelizePairs(list);
        JavaPairRDD<String, Iterable<String>> rdd2 = rdd.groupByKey();
        rdd2.foreach(new VoidFunction<Tuple2<String,Iterable<String>>>() {
            
            @Override
            public void call(Tuple2<String, Iterable<String>> t) throws Exception {
                String s = t._1;
                Iterator<String> iter = t._2.iterator();
                String person = "";
                while(iter.hasNext()) {
                    person = person + iter.next()+" ";
                }
                System.out.println("职业:"+s+",人员:"+person);
            }
        });
        
    }

(2)使用scala编写

  def groupByKey: Unit = {
    val list = List(
            new Tuple2("Doctor","A"),
                    new Tuple2("Actor","B"),
                    new Tuple2("Doctor","C"),
                    new Tuple2("Actor","D")
                            )
                            
    val rdd = sc.parallelize(list)
    rdd.groupByKey().foreach(t =>{
        val s = t._1
        val iter = t._2.iterator
        var person = ""
        while(iter.hasNext) 
          person = person + iter.next + " "
        println("职业:"+s+",人员:"+person)
        
      }
    )
  }

(3)结果

3.3 join

join是将两个PairRDD合并,并将有相同key的元素分为一组,可以理解为groupByKey和Union的结合

(1)使用Java编写

    public static void join() {
        List<Tuple2<Integer,String>> list1 = Arrays.asList(
                    new Tuple2<Integer,String>(1,"小明"),
                    new Tuple2<Integer,String>(2,"小米"),
                    new Tuple2<Integer,String>(3,"晓红"),
                    new Tuple2<Integer,String>(4,"小绿")
                );
        List<Tuple2<Integer,Integer>> list2 = Arrays.asList(
                new Tuple2<Integer,Integer>(1,88),
                new Tuple2<Integer,Integer>(2,99),
                new Tuple2<Integer,Integer>(3,100),
                new Tuple2<Integer,Integer>(4,98)
            );
        JavaPairRDD<Integer, String> rdd1 = jsc.parallelizePairs(list1);
        JavaPairRDD<Integer, Integer> rdd2 = jsc.parallelizePairs(list2);
        JavaPairRDD<Integer, Tuple2<String, Integer>> rdd = rdd1.join(rdd2);
        rdd.foreach(new VoidFunction<Tuple2<Integer,Tuple2<String,Integer>>>() {
            
            @Override
            public void call(Tuple2<Integer, Tuple2<String, Integer>> tuple) throws Exception {
                System.out.println("学号:"+tuple._1+",姓名:"+tuple._2._1+",分数:"+tuple._2._2);
            }
        });
    }

(2)使用scala编写

  def join: Unit = {
    val list1 = List(
            new Tuple2(1,"小明"),
                    new Tuple2(2,"小米"),
                    new Tuple2(3,"晓红"),
                    new Tuple2(4,"小绿")  
      )
    val list2 = List(
          new Tuple2(1,88),
                  new Tuple2(2,99),
                  new Tuple2(3,100),
                  new Tuple2(4,98)  
      )
      val rdd1 = sc.parallelize(list1)
      val rdd2 = sc.parallelize(list2)
      rdd1.join(rdd2).foreach(tuple => {
        println("学号:"+tuple._1+",姓名:"+tuple._2._1+",分数:"+tuple._2._2)
      })
  }

(3)结果

四、distinct、intersection、subtract

4.1 distinct([numTasks])

返回一个在源数据集去重之后的新数据集,即去重,并局部无序而整体有序返回。

(1)使用Java编写

    public static void distinct() {
        List<Integer> list = Arrays.asList(1,1,2,3,4,55,55,6,4,32,2,1,3);
        jsc.parallelize(list).distinct().foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)使用scala编写

  def distinct: Unit = {
    val list = List(1,1,2,3,4,55,55,6,4,32,2,1,3)
    sc.parallelize(list).distinct().foreach(println)
  }

(3)结果

 

4.2 intersection

对于源数据集和其他数据集求交集,并去重,且无序返回。

(1)使用Java编写

    public static void intersection() {
        List<Integer> list1 = Arrays.asList(1,2,3,4);
        List<Integer> list2 = Arrays.asList(3,4,5,6,1,3);
        
        JavaRDD<Integer> rdd1 = jsc.parallelize(list1);
        JavaRDD<Integer> rdd2 = jsc.parallelize(list2);
        rdd1.intersection(rdd2).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)使用scala编写

  def intersection: Unit = {
    val list1 = List(1,2,3,4)
        val list2 = List(3,4,5,6,1,3)
        val rdd1 = sc.parallelize(list1)
        val rdd2 = sc.parallelize(list2)
        rdd1.intersection(rdd2).foreach(println)
  }

(3)结果

4.3 subtract

subtract相当于进行集合的差操作,RDD 1去除RDD 1和RDD 2交集中的所有元素。

(1)使用Java编写

    public static void subtract() {
        List<Integer> list1 = Arrays.asList(1,2,3,4,11,12);
        List<Integer> list2 = Arrays.asList(3,4,5,6,1,3);
        
        JavaRDD<Integer> rdd1 = jsc.parallelize(list1);
        JavaRDD<Integer> rdd2 = jsc.parallelize(list2);
        rdd1.subtract(rdd2).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

 

五、coalesce、repartition、repartitionAndSortWithinPartitions

5.1 coalesce

 该函数用于将RDD进行重分区,使用HashPartitioner。 
重新分区,减少RDD中分区的数量到numPartitions。

(1)使用Java编写

    public static void coalesce() {
        JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1,3,45,2,4,542,2),3);
        rdd.coalesce(1).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

5.2   repartition

进行重分区,解决的问题:本来分区数少  -》 增加分区数

(1)使用Java编写

    public static void repartition() {
        jsc.parallelize(Arrays.asList(1,3,4,24,14,421,1),2).repartition(4).foreach(new VoidFunction<Integer>() {
            
            @Override
            public void call(Integer s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

5.3 repartitionAndSortWithinPartitions

repartitionAndSortWithinPartitions函数是repartition函数的变种,与repartition函数不同的是,repartitionAndSortWithinPartitions在给定的partitioner内部进行排序,性能比repartition要高。 

(1)使用Java编写

    public static void repartitionAndSortWithinPartitions() {
        JavaPairRDD<Integer, Integer> rdd = jsc.parallelize(Arrays.asList(1,3,4,24,14,421,1),1)
                .mapToPair(new PairFunction<Integer, Integer, Integer>() {

                    private static final long serialVersionUID = 1L;

                    @Override
                    public Tuple2<Integer, Integer> call(Integer s) throws Exception {
                        return new Tuple2<Integer, Integer>(s, s);
                    }
                });
        JavaPairRDD<Integer, Integer> rdd2 = rdd.repartitionAndSortWithinPartitions(new Partitioner() {
            
            private static final long serialVersionUID = 1L;

            @Override
            public int numPartitions() {
                return 2;
            }
            
            @Override
            public int getPartition(Object key) {
                Integer p = Integer.valueOf(key.toString());
                if(p%2==0) {
                    return 0;
                }else {
                    return 1;
                }
            }
        });
        JavaRDD<String> rdd3 = rdd2.mapPartitionsWithIndex(new Function2<Integer, Iterator<Tuple2<Integer,Integer>>, Iterator<String>>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterator<String> call(Integer index, Iterator<Tuple2<Integer, Integer>> iter) throws Exception {
                List<String> list = new ArrayList<>();
                while(iter.hasNext()) {
                    list.add(index+" "+iter.next());
                }
                return list.iterator();
            }
        }, true);
        rdd3.foreach(new VoidFunction<String>() {
            
            @Override
            public void call(String s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

六、cogroup、sortBykey、aggregateByKey

6.1 cogroup

对两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。与reduceByKey不同的是针对两个RDD中相同的key的元素进行合并。

(1)使用Java编写

    public static void cogroup() {
        List<Tuple2<Integer, String>> list1 = Arrays.asList(
                new Tuple2<Integer, String>(1, "张三"),
                new Tuple2<Integer, String>(2, "李四")
        );

        List<Tuple2<Integer, String>> list2 = Arrays.asList(
                new Tuple2<Integer, String>(1, "王五"),
                new Tuple2<Integer, String>(2, "赵柳"),
                new Tuple2<Integer, String>(3, "田七")
        );

        List<Tuple2<Integer, String>> list3 = Arrays.asList(
                new Tuple2<Integer, String>(1, "张三"),
                new Tuple2<Integer, String>(2, "李四"),
                new Tuple2<Integer, String>(3, "尾巴")
        );

        JavaPairRDD<Integer, String> list1RDD = jsc.parallelizePairs(list1);
        JavaPairRDD<Integer, String> list2RDD = jsc.parallelizePairs(list2);
        JavaPairRDD<Integer, String> list3RDD = jsc.parallelizePairs(list3);
        
        list1RDD.cogroup(list2RDD,list3RDD).foreach(new VoidFunction<Tuple2<Integer,Tuple3<Iterable<String>,Iterable<String>,Iterable<String>>>>() {
            
            @Override
            public void call(Tuple2<Integer, Tuple3<Iterable<String>, Iterable<String>, Iterable<String>>> s)
                    throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

 

6.2 sortBykey

sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序。它是在org.apache.spark.rdd.OrderedRDDFunctions中实现的,实现如下

def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.size)
    : RDD[(K, V)] =
{
  val part = new RangePartitioner(numPartitions, self, ascending)
  new ShuffledRDD[K, V, V](self, part)
    .setKeyOrdering(if (ascending) ordering else ordering.reverse)
}

从函数的实现可以看出,它主要接受两个函数,含义和sortBy一样,这里就不进行解释了。该函数返回的RDD一定是ShuffledRDD类型的,因为对源RDD进行排序,必须进行Shuffle操作,而Shuffle操作的结果RDD就是ShuffledRDD。其实这个函数的实现很优雅,里面用到了RangePartitioner,它可以使得相应的范围Key数据分到同一个partition中,然后内部用到了mapPartitions对每个partition中的数据进行排序,而每个partition中数据的排序用到了标准的sort机制,避免了大量数据的shuffle。参数主要有两个,一是指定升降序排序,默认是true,二是指定分区数,默认是父rdd的分区数。下面对sortByKey的使用进行说明:

(1)使用Java编写

    public static void sortByKey() {
        JavaRDD<String> rdd = jsc.textFile("words");
        JavaPairRDD<String, Integer> rdd2 = rdd.flatMap(new FlatMapFunction<String, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterable<String> call(String s) throws Exception {
                return Arrays.asList(s.split(" "));
            }
        }).mapToPair(new PairFunction<String, String, Integer>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<String, Integer>(s, 1);
            }
        });
        JavaPairRDD<String, Integer> rdd3 = rdd2.reduceByKey(new Function2<Integer, Integer, Integer>() {
            
            private static final long serialVersionUID = 1L;

            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1+v2;
            }
        });
        rdd3.mapToPair(new PairFunction<Tuple2<String,Integer>, Integer, String>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Tuple2<Integer, String> call(Tuple2<String, Integer> t) throws Exception {
                return new Tuple2<Integer, String>(t._2, t._1);
            }
        }).sortByKey(false).mapToPair(new PairFunction<Tuple2<Integer,String>, String, Integer>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Tuple2<String, Integer> call(Tuple2<Integer, String> t) throws Exception {
                return new Tuple2<String, Integer>(t._2, t._1);
            }
        }).foreach(new VoidFunction<Tuple2<String,Integer>>() {
            
            @Override
            public void call(Tuple2<String, Integer> s) throws Exception {
                System.out.println(s);
            }
        });
    }

(2)结果

6.3 aggregateByKey

aggregateByKey函数对PairRDD中相同Key的值进行聚合操作,在聚合过程中同样使用了一个中立的初始值。和aggregate函数类似,aggregateByKey返回值的类型不需要和RDD中value的类型一致。因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey函数最终返回的类型还是Pair RDD,对应的结果是Key和聚合好的值;而aggregate函数直接是返回非RDD的结果,这点需要注意。在实现过程中,定义了三个aggregateByKey函数原型,但最终调用的aggregateByKey函数都一致。

(1)使用Java编写

    public static void aggregateByKey() {
        List<Tuple2<String, Integer>> list = Arrays.asList(
                new Tuple2<>("cat", 10),
                new Tuple2<>("mouse",22),
                new Tuple2<>("mouse", 5),
                new Tuple2<>("cat", 10),
                new Tuple2<>("mouse",22),
                new Tuple2<>("dog", 5)
                );
        JavaPairRDD<String, Integer> rdd = jsc.parallelizePairs(list,2);
        rdd.mapPartitionsWithIndex(new Function2<Integer, Iterator<Tuple2<String,Integer>>, Iterator<Tuple2<String,Integer>>>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterator<Tuple2<String, Integer>> call(Integer index
                    , Iterator<Tuple2<String, Integer>> iter)
                    throws Exception {
                while(iter.hasNext()) {
                    System.out.println("partitionIndex:"+index+"---->"+iter.next());
                }
                return iter;
            }
        }, true).collect();
        /*
            partitionIndex:0---->(cat,10)
            partitionIndex:0---->(mouse,22)
            partitionIndex:0---->(mouse,5)
            
            partitionIndex:1---->(cat,10)
            partitionIndex:1---->(mouse,22)
            partitionIndex:1---->(dog,5)
            
            该数据源被分成两个partition
        */
        /**
         * 该函数第一个参数为起始值,该值在一个函数执行时,会在每个key的values中加入该值
         */
        JavaPairRDD<String, Integer> rdd2 = rdd.aggregateByKey(100, new Function2<Integer, Integer, Integer>() {

            private static final long serialVersionUID = 1L;
            /*
             * 该函数统计同个partition内的数据
             * */
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return Math.max(v1, v2);
            }
        }, new Function2<Integer, Integer, Integer>() {

            private static final long serialVersionUID = 1L;
            /**
             * 该函数统计不同partition内的数据
             */
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1+v2;
            }
        });
        rdd2.foreach(new VoidFunction<Tuple2<String,Integer>>() {
            
            @Override
            public void call(Tuple2<String, Integer> s) throws Exception {
                System.out.println(s);
            }
        });
        
    }

(2)结果

6.4 combineByKey

 combineByKey()是最为常用的基于键进行聚合的函数。大多数基于键聚合的函数都是用它实现的。和aggregate()一样,combineByKey()可以让用户返回与输入数据的类型不同的返回值。

要理解combineByKey(),要先理解它在处理数据时是如何处理每个元素的。由于combineByKey()会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。

如果这是一个新的元素,combineByKey()会使用一个叫做createCombiner()的函数来创建那个键对应的累加器的初始值。需要注意的是,这个过程会在每个分区中第一次出现各个键时发生,而不是在整个RDD中第一次出现一个键时发生。

如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并。

由于每个分区都是独立处理的,因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器,就需要使用用户提供的mergeCombiners()方法将各个分区的结果合并。

(1)使用Java编写

    public static void combineByKey() {
        List<Tuple2<String, Integer>> list = Arrays.asList(
                new Tuple2<>("晓红",11 ),
                new Tuple2<>("小绿",12 ),
                new Tuple2<>("小黑",13 ),
                new Tuple2<>("晓红",14 ),
                new Tuple2<>("小绿",15 ),
                new Tuple2<>("晓红",16 )
                );
        JavaPairRDD<String, Integer> rdd = jsc.parallelizePairs(list,2);
        rdd.mapPartitionsWithIndex(new Function2<Integer, Iterator<Tuple2<String,Integer>>, Iterator<Tuple2<String,Integer>>>() {

            private static final long serialVersionUID = 1L;

            @Override
            public Iterator<Tuple2<String, Integer>> call(Integer index,
                    Iterator<Tuple2<String, Integer>> iter) throws Exception {
                List<Tuple2<String, Integer>> list = new ArrayList<Tuple2<String, Integer>>();
                while(iter.hasNext()){
                    Tuple2<String, Integer> next = iter.next();
                    System.out.println("partitionindex ="+index+",value="+next);
                    list.add(next);
                }
                return list.iterator();
            }

        }, true).collect();
        /**数据分区结果:
            partitionindex =0,value=(晓红,11)
            partitionindex =0,value=(小绿,12)
            partitionindex =0,value=(小黑,13)
            
            partitionindex =1,value=(晓红,14)
            partitionindex =1,value=(小绿,15)
            partitionindex =1,value=(晓红,16)
         */
        
        rdd.combineByKey(new Function<Integer, Integer>() {
            
            /**
             * 该方法与aggregateByKey相似(底层貌似是combineByKey)
             * 该方法的第一个参数的方法实现数据转化的功能,可以将收到的数据转化成任意类型的数据
             */
            private static final long serialVersionUID = 1L;

            @Override
            public Integer call(Integer a) throws Exception {
                return a;
            }
        }, new Function2<Integer,Integer,Integer>() {
            /**
             * 第二个参数的方法处理来自同一个partition内的数据
             */
            private static final long serialVersionUID = 1L;

            @Override
            public Integer call(Integer a, Integer b) throws Exception {
                return Math.max(a, b);
            }
        }, new Function2<Integer,Integer,Integer>() {
            /**
             * 第三个参数的方法处理来自不同partition的数据
             */
            private static final long serialVersionUID = 1L;

            @Override
            public Integer call(Integer a, Integer b) throws Exception {
                return a+b;
            }
        }).foreach(new VoidFunction<Tuple2<String,Integer>>() {
            
            @Override
            public void call(Tuple2<String, Integer> a) throws Exception {
                System.out.println(a);
            }
        });
    }

(2)结果

七、zip相关算子

 

posted @ 2019-02-24 23:32  kpsmile  阅读(709)  评论(0编辑  收藏  举报