SparkJavaAPI例子

1、join 的使用

将一组数据转化为RDD后,分别创造出两个PairRDD,然后再对两个PairRDD进行归约(即合并相同Key对应的Value),

元素集合1:  {(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)}

元素集合2: {(1, A), (2, D), (3, I), (4, P), (5, Y)}

集合1和集合2进行join:

{(1, (1, A)), (2, (4, D)), (3, (9, I)), (4, (16, P)), (5, (25, Y))}

过程如下图所示:

示例代码:

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;

import scala.Tuple2;

public class JoinDemo {
	
	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("JoinDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 3, 4, 5);
		JavaRDD<Integer> rdd = sc.parallelize(data);
		
		// FirsrRDD
		JavaPairRDD<Integer, Integer> firstRDD = rdd.mapToPair(
				new PairFunction<Integer, Integer, Integer>() {
					
					private static final long serialVersionUID = 1L;

					@Override
					public Tuple2<Integer, Integer> call(Integer num) throws Exception {
						return new Tuple2<Integer, Integer>(num, num * num);
					}
				});
		
		// SecondRDD
		JavaPairRDD<Integer, String> secondRDD = rdd.mapToPair(
				new PairFunction<Integer, Integer, String>() {
					
					private static final long serialVersionUID = 1L;

					@Override
					public Tuple2<Integer, String> call(Integer num) throws Exception {
						return new Tuple2<>(num, String.valueOf((char)(64 + num * num)));
					}
				});
		
//		JavaPairRDD<Integer, Tuple2<Integer, String>> joinRDD = firstRDD.join(secondRDD); 
//		   
//        JavaRDD<String> res = joinRDD.map(new Function<Tuple2<Integer, Tuple2<Integer, String>>, String>() { 
//            @Override 
//            public String call(Tuple2<Integer, Tuple2<Integer, String>> integerTuple2Tuple2) throws Exception { 
//                int key = integerTuple2Tuple2._1(); 
//                int value1 = integerTuple2Tuple2._2()._1(); 
//                String value2 = integerTuple2Tuple2._2()._2(); 
//                return "<" + key + ",<" + value1 + "," + value2 + ">>"; 
//            } 
//        }); 
		
		JavaPairRDD<Integer, Tuple2<Integer, String>> joinRDD = firstRDD.join(secondRDD);
		
		JavaRDD<String> resultRDD = joinRDD.map(new Function<Tuple2<Integer, Tuple2<Integer, String>>, String> () {
			
			private static final long serialVersionUID = 1L;

			@Override
			public String call(Tuple2<Integer, Tuple2<Integer, String>> tuple) throws Exception {
				int key = tuple._1;
				String value = tuple._2._1 + ", " + tuple._2._2;
				return "<" + key + ", <" + value +">>";
			}
		});
		
		resultRDD.foreach(new VoidFunction<String>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public void call(String str) throws Exception {
				System.out.println(str);
			}
		});
		
		
//		List<String> resultList = resultRDD.collect();
//		
//		for (String str : resultList) {
//			System.out.println(str);
//		}
		
		sc.stop();
	}
}

运行结果如下:

<4, <16, P>>
<1, <1, A>>
<3, <9, I>>
<5, <25, Y>>
<2, <4, D>>

2.cogroup的使用

cogroup就是:

有两个元组Tuple的集合A与B,先对A组集合中key相同的value进行聚合,  然后对B组集合中key相同的value进行聚合,之后对A组与B组进行"join"操作;  

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;

import scala.Tuple2;

public class CogroupDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("JoinDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Tuple2<Integer, String>> nameList = Arrays.asList(
				new Tuple2<Integer, String>(1, "Hadoop"),
				new Tuple2<Integer, String>(2, "Hive"),
				new Tuple2<Integer, String>(3, "Spark"),
				new Tuple2<Integer, String>(4, "Storm"),
				new Tuple2<Integer, String>(5, "HBase"));
		
		List<Tuple2<Integer, Integer>> scoreList = Arrays.asList(
				new Tuple2<Integer, Integer>(1, 88),
				new Tuple2<Integer, Integer>(2, 93),
				new Tuple2<Integer, Integer>(3, 81),
				new Tuple2<Integer, Integer>(4, 88),
				new Tuple2<Integer, Integer>(5, 90));
		
		JavaPairRDD<Integer, String> nameRDD = sc.parallelizePairs(nameList);
		
		JavaPairRDD<Integer, Integer> scoreRDD = sc.parallelizePairs(scoreList);

		JavaPairRDD<Integer, Tuple2<Iterable<String>, Iterable<Integer>>> nameScoreRDD = nameRDD.cogroup(scoreRDD); 
		
		nameScoreRDD.foreach(new VoidFunction<Tuple2<Integer, Tuple2<Iterable<String>, Iterable<Integer>>>>() {
            
        	private static final long serialVersionUID = 1L;
            
            @Override
            public void call(Tuple2<Integer, Tuple2<Iterable<String>, Iterable<Integer>>> tuple) {
                    String str = "ID: " + tuple._1 + ", Name: " + tuple._2._1 + ", Score: " + tuple._2._2;
                    
                    System.out.println(str);
            }
        });
		
		sc.close();
	}
}

运行结果:

ID: 1, Name: [Hadoop], Score: [88]
ID: 3, Name: [Spark], Score: [81]
ID: 5, Name: [HBase], Score: [90]
ID: 2, Name: [Hive], Score: [93]

3、GroupByKey的使用

把相同key对应的value收集到一起,完成一些运算(例如拼接字符串,或者去重) 

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class GroupByKeyDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("GroupByKeyDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 1, 2, 2, 1);
		JavaRDD<Integer> distData = sc.parallelize(data);
		
		JavaPairRDD<Integer, Integer> firstRDD = distData.mapToPair(new PairFunction<Integer, Integer, Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Tuple2<Integer, Integer> call(Integer num) throws Exception {
				return new Tuple2<Integer, Integer>(num, num * num);
			}
		});
		
		JavaPairRDD<Integer, Iterable<Integer>> secondRDD = firstRDD.groupByKey();
		
		List<Tuple2<Integer, String>> resultList = secondRDD.map(new Function<Tuple2<Integer, Iterable<Integer>>, Tuple2<Integer, String>>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Tuple2<Integer, String> call(Tuple2<Integer, Iterable<Integer>> tuple) throws Exception {
				int key = tuple._1;
				Iterator<Integer> iter = tuple._2.iterator();
				
				StringBuffer sb = new StringBuffer();
				
				while (iter.hasNext()) {
					sb.append(iter.next()).append(" ");
				}
				
				return new Tuple2<Integer, String>(key, sb.toString().trim());
			}
		}).collect();
		
        for(Tuple2<Integer, String> tuple : resultList) {
            System.out.println(tuple._1 + "\t" + tuple._2);
        }
		
		sc.stop();
	}
}

运行结果:

1	1 1 1
2	4 4

补充1  引自:http://blog.csdn.net/zongzhiyuan/article/details/49965021

在spark中,我们知道一切的操作都是基于RDD的。在使用中,RDD有一种非常特殊也是非常实用的format——pair RDD,即RDD的每一行是(key, value)的格式。这种格式很像Python的字典类型,便于针对key进行一些处理。

针对pair RDD这样的特殊形式,spark中定义了许多方便的操作;

首先,看一看spark官网[1]是怎么解释的:

reduceByKey(func, numPartitions=None)

 

Merge the values for each key using an associative reduce function. This will also perform the merginglocally on each mapper before sending results to a reducer, similarly to a “combiner” in MapReduce. Output will be hash-partitioned with numPartitions partitions, or the default parallelism level if numPartitions is not specified.

也就是,reduceByKey用于对每个key对应的多个value进行merge操作,最重要的是它能够在本地先进行merge操作,并且merge操作可以通过函数自定义。

groupByKey(numPartitions=None)

Group the values for each key in the RDD into a single sequence. Hash-partitions the resulting RDD with numPartitions partitions. Note: If you are grouping in order to perform an aggregation (such as a sum or average) over each key, using reduceByKey or aggregateByKey will provide much better performance.

也就是,groupByKey也是对每个key进行操作,但只生成一个sequence。需要特别注意“Note”中的话,它告诉我们:如果需要对sequence进行aggregation操作(注意,groupByKey本身不能自定义操作函数),那么,选择reduceByKey/aggregateByKey更好。这是因为groupByKey不能自定义函数,我们需要先用groupByKey生成RDD,然后才能对此RDD通过map进行自定义函数操作。

为了更好的理解上面这段话,下面我们使用两种不同的方式去计算单词的个数[2]:

val words = Array("one", "two", "two", "three", "three", "three")  
  
val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))  
  
val wordCountsWithReduce = wordPairsRDD.reduceByKey(_ + _)  
  
val wordCountsWithGroup = wordPairsRDD.groupByKey().map(t => (t._1, t._2.sum)) 

上面得到的wordCountsWithReduce和wordCountsWithGroup是完全一样的,但是,它们的内部运算过程是不同的。

(1)当采用reduceByKeyt时,Spark可以在每个分区移动数据之前将待输出数据与一个共用的key结合。借助下图可以理解在reduceByKey里究竟发生了什么。 注意在数据对被搬移前同一机器上同样的key是怎样被组合的(reduceByKey中的lamdba函数)。然后lamdba函数在每个区上被再次调用来将所有值reduce成一个最终结果。整个过程如下:

(2)当采用groupByKey时,由于它不接收函数,spark只能先将所有的键值对(key-value pair)都移动,这样的后果是集群节点之间的开销很大,导致传输延时。整个过程如下:

因此,在对大数据进行复杂计算时,reduceByKey优于groupByKey。

另外,如果仅仅是group分组处理,那么以下函数应该优先于 groupByKey :
  (1)、combineByKey 组合数据,但是组合之后的数据类型与输入时值的类型不一样。
  (2)、foldByKey合并每一个 key 的所有值,在级联函数和“零值”中使用。

4、map的使用

数据集中的每个元素经过用户自定义的函数转换形成一个新的RDD,新的RDD叫MappedRDD

将函数应用于RDD中的每个元素,将返回值构成新的RDD

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;

public class MapDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("MapDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 3, 5, 7);
		
		
		JavaRDD<Integer> rdd = sc.parallelize(data);
		
		JavaRDD<Integer> resultRDD = rdd.map(new Function<Integer, Integer>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Integer call(Integer value) throws Exception {
				return value * value;
			}
		});
		
		resultRDD.foreach(new VoidFunction<Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public void call(Integer value) throws Exception {
				System.out.println(value);
			}
		});
		
		sc.stop();
	}
}

运行结果:

1
9
25
49

5、flatmap的使用

将函数应用于RDD中的每个元素,将返回的迭代器的所有内容构成新的RDD,通常用来切分单词。与map的区别是:这个函数返回的值是list的一个,去除原有的格式

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;

public class FlatMapDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("FlatMapDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		JavaRDD<String> lineRDD = sc.parallelize(Arrays.asList("hello world", "good morning"));
		JavaRDD<String> wordRDD = lineRDD.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(" "));
			}
		});
		
		List<String> wordList = wordRDD.collect();
		
		for (String word : wordList) {
			System.out.println(word);
		}
		
		sc.stop();
	}
}

运行结果:

hello
world
good
morning

6、mapPartitions的使用

mapPartitions函数会对每个分区依次调用分区函数处理,然后将处理的结果(若干个Iterator)生成新的RDDs。
mapPartitions与map类似,但是如果在映射的过程中需要频繁创建额外的对象,使用mapPartitions要比map高效的过。比如,将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,可能要为每一个元素都创建一个connection,这样开销很大,如果使用mapPartitions,那么只需要针对每一个分区建立一个connection。

两者的主要区别是调用的粒度不一样:map的输入变换函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区。

 假设一个rdd有10个元素,分成3个分区。如果使用map方法,map中的输入函数会被调用10次;而使用mapPartitions方法的话,其输入函数会只会被调用3次,每个分区调用1次。

rdd的mapPartitions是map的一个变种,它们都可进行分区的并行处理。

    两者的主要区别是调用的粒度不一样:map的输入变换函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区。

    假设一个rdd有10个元素,分成3个分区。如果使用map方法,map中的输入函数会被调用10次;而使用mapPartitions方法的话,其输入函数会只会被调用3次,每个分区调用1次。

    //生成10个元素3个分区的rdd a,元素值为1~10的整数(1 2 3 4 5 6 7 8 9 10),sc为SparkContext对象

    val a = sc.parallelize(1 to 10, 3)

    //定义两个输入变换函数,它们的作用均是将rdd a中的元素值翻倍

    //map的输入函数,其参数e为rdd元素值   

    def myfuncPerElement(e:Int):Int = {

           println("e="+e)

           e*2

      }

     //mapPartitions的输入函数。iter是分区中元素的迭代子,返回类型也要是迭代子

    def myfuncPerPartition ( iter : Iterator [Int] ) : Iterator [Int] = {

         println("run in partition")

         var res = for (e <- iter ) yield e*2

          res

    }

    val b = a.map(myfuncPerElement).collect

    val c =  a.mapPartitions(myfuncPerPartition).collect

    在spark shell中运行上述代码,可看到打印了3次run in partition,打印了10次e=。

      从输入函数(myfuncPerElement、myfuncPerPartition)层面来看,map是推模式,数据被推到myfuncPerElement中;mapPartitons是拉模式,myfuncPerPartition通过迭代子从分区中拉数据。

    这两个方法的另一个区别是在大数据集情况下的资源初始化开销和批处理处理,如果在myfuncPerPartition和myfuncPerElement中都要初始化一个耗时的资源,然后使用,比如数据库连接。在上面的例子中,myfuncPerPartition只需初始化3个资源(3个分区每个1次),而myfuncPerElement要初始化10次(10个元素每个1次),显然在大数据集情况下(数据集中元素个数远大于分区数),mapPartitons的开销要小很多,也便于进行批处理操作。

   mapPartitionsWithIndex和mapPartitons类似,只是其参数多了个分区索引号。

案例代码:

package com.spark.api.spark_java_api_learn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;

public class MapPartitionsDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("MapPartitionsDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		
		// 3个分区
		JavaRDD<Integer> numRDD = sc.parallelize(data, 3);
		
		JavaRDD<Integer> newRDD = numRDD.map(new Function<Integer, Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Integer call(Integer value) throws Exception {
				
				System.out.println("map");
				
				return value * 2;
			}
		});
		
		JavaRDD<Integer> newPartitionRDD = numRDD.mapPartitions(new FlatMapFunction<Iterator<Integer>, Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Iterable<Integer> call(Iterator<Integer> tuple) throws Exception {
				
				List<Integer> value = new ArrayList<>();
				
				while (tuple.hasNext()) {
					value.add(tuple.next() * 2);
				}
				
				System.out.println("mapPartitions");
				
				return value;
			}
		});
		
		newRDD.foreach(new VoidFunction<Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public void call(Integer value) throws Exception {
				System.out.println(value);
			}
		});
		
		System.out.println("==========================================================");
		
		newPartitionRDD.foreach(new VoidFunction<Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public void call(Integer value) throws Exception {
				System.out.println(value);
			}
		});
		
		sc.stop();
	}
}

运行结果:

可以看到打印了 10 个 map 以及 3 个 mapPartitions

7、mapPartitionsWithIndex的使用

mapPartitionsWithIndex与mapPartitions基本相同,只是在处理函数的参数是一个二元元组,元组的第一个元素是当前处理的分区的index,元组的第二个元素是当前处理的分区元素组成的Iterator

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.VoidFunction;

public class MapPartitionsWithIndexDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("MapPartitionsWithIndexDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 4, 3, 5, 6, 7);
		
		//RDD有两个分区
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 2);
		
		//分区index、元素值、元素编号输出
		JavaRDD<String> mapPartitionsWithIndexRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<Integer>, Iterator<String>>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer value1, Iterator<Integer> value2) throws Exception {
				LinkedList<String> linkedList = new LinkedList<String>();

				while (value2.hasNext()) {
					linkedList.add(Integer.toString(value1) + " | " + value2.next().toString());
				}
				
				return linkedList.iterator();
			}
        // mapPartitionsWithIndex的第二个参数,表示是否保留父RDD的partitioner分区 
		}, false);
		
		mapPartitionsWithIndexRDD.foreach(new VoidFunction<String>() {
			
			private static final long serialVersionUID = 1L;

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

运行结果:

0 | 1
0 | 2
0 | 4
1 | 3
1 | 5
1 | 6
1 | 7

8、sortBy的使用

根据给定的f函数将RDD中的元素进行排序。
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;


public class SortByDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("SortByDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		// 字符串由2部分拼接,下划线前面部分是字符串,后面是数字
		List<String> data = Arrays.asList(
				"Sky_89",
				"Silva_78",
				"Cici_91",
				"Hua_67",
				"Seven_81",
				"Tao_84",
				"Lucky_85",
				"Ryzen_77",
				"Intel_80");
		
		JavaRDD<String> javaRDD = sc.parallelize(data, 3);
		
		//按RDD中每个元素的第二部分进行排序
		JavaRDD<String> resultRDD = javaRDD.sortBy(new Function<String, String>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public String call(String value) throws Exception {
				// 返回v1作为比较值
				return value.split("_")[1];
			}
			// 第二个boolean型参数,true表示升序,false表示降序
			// 第三个参数是分区数,必填
		}, false, 3);
		
		resultRDD.foreach(new VoidFunction<String>() {
			
			private static final long serialVersionUID = 1L;

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

运行结果:

Cici_91
Sky_89
Lucky_85
Tao_84
Seven_81
Intel_80
Silva_78
Ryzen_77
Hua_67

9、takeOrdered的使用

takeOrdered函数用于从RDD中,按照默认(升序)或指定排序规则,返回前num个元素
package com.spark.api.spark_java_api_learn;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

public class TakeOrderedDemo {
	
	// 要实现接口Serializable和Comparator
	public static class TakeOrderedComparator implements Serializable, Comparator<Integer>{
	    @Override
	    public int compare(Integer o1, Integer o2) {
	        // 降序
	        return -o1.compareTo(o2);
	    }
	}
	
	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("SortByDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 0, 4, 4, 2, 2);
		
		// 3个分区
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 3);
		
		// take 表示在不排序,返回前2个元素   [5, 1]
		System.out.println("take-----1-------------" + javaRDD.take(2));
		
		// takeOrdered,默认升序排序,取前2个  [0, 1]
		System.out.println("takeOrdered-----1-------------" + javaRDD.takeOrdered(2));
		
		// takeOrdered,给定的排序规则,降序排序,取前2个  [5, 4]
		List<Integer> list = javaRDD.takeOrdered(2, new TakeOrderedComparator());
		
		System.out.println("takeOrdered----2--------------" + list);
		
		sc.stop();
	}
}

10、takeSample的使用

takeSample函数返回一个数组,在数据集中随机采样 num 个元素组成。

该方法仅在预期结果数组很小的情况下使用,因为所有数据都被加载到driver的内存中。

takeSample函数类似于sample函数,该函数接受三个参数,第一个参数withReplacement ,表示采样是否放回,true表示有放回的采样,false表示无放回采样;第二个参数num,表示返回的采样数据的个数,这个也是takeSample函数和sample函数的区别;第三个参数seed,表示用于指定的随机数生成器种子。另外,takeSample函数先是计算fraction,也就是采样比例,然后调用sample函数进行采样,并对采样后的数据进行collect(),最后调用take函数返回num个元素。注意,如果采样个数大于RDD的元素个数,且选择的无放回采样,则返回RDD的元素的个数。

1、withReplacement:元素可以多次抽样(在抽样时替换)

2、num:返回的样本的大小

3、seed:随机数生成器的种子(建议第三个参数seed可以默认,不好把控。)

(1)当不可以多次抽样:withReplacement=false;样本个数num大于父本个数时,只能返回父本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(false, 8);
System.out.println(res);

//结果:[6, 8, 2, 4]

(2)当不可以多次抽样:withReplacement=false;样本个数num小于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(false, 3);
System.out.println(res);

//结果:[8, 4, 2]

(3)当可以多次抽样:withReplacement=true;样本个数num大于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(true, 8);
System.out.println(res);

//结果:[4, 6, 8, 2, 6, 2, 4, 2]

(4)当不可以多次抽样:withReplacement=false;样本个数num小于父本个数时,返回样本个数

JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(6, 2, 8, 4));
List<Integer> res = rdd.takeSample(false, 3);
System.out.println(res);

//结果:[8, 8, 2]

11、distinct的使用

distinct() 功能是 deduplicate RDD 中的所有的重复数据。由于重复数据可能分散在不同的 partition 里面,因此需要 shuffle 来进行 aggregate 后再去重。然而,shuffle 要求数据类型是 <K, V> 。如果原始数据只有 Key(比如例子中 record 只有一个整数),那么需要补充成 <K, null> 。这个补充过程由 map() 操作完成,生成 MappedRDD。然后调用上面的 reduceByKey() 来进行 shuffle,在 map 端进行 combine,然后 reduce 进一步去重,生成 MapPartitionsRDD。最后,将 <K, null> 还原成 K,仍然由 map() 完成,生成 MappedRDD。

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

public class DistinctDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("SortByDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 4, 3, 5, 6, 7, 1, 2);
		JavaRDD<Integer> javaRDD = sc.parallelize(data);

		JavaRDD<Integer> distinctRDD1 = javaRDD.distinct();
		
		// [4, 1, 6, 3, 7, 5, 2]
		System.out.println(distinctRDD1.collect());
		
		// 指定分区数
		JavaRDD<Integer> distinctRDD2 = javaRDD.distinct(2);
		
		// [4, 6, 2, 1, 3, 7, 5]
		System.out.println(distinctRDD2.collect());
		
		sc.stop();
	}
}

12、cartesian的使用

Cartesian 对两个 RDD 做笛卡尔集,生成的 CartesianRDD 中 partition 个数 = partitionNum(RDD a) * partitionNum(RDD b)。从getDependencies分析可知,这里的依赖关系与前面的不太一样,CartesianRDD中每个partition依赖两个parent RDD,而且其中每个 partition 完全依赖(NarrowDependency) RDD a 中一个 partition,同时又完全依赖(NarrowDependency) RDD b 中另一个 partition。具体如下CartesianRDD 中的 partiton i 依赖于 (RDD a).List(i / numPartitionsInRDDb) 和 (RDD b).List(i % numPartitionsInRDDb)。

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

public class CartesianDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("CartesianDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 3, 4);
		JavaRDD<Integer> javaRDD = sc.parallelize(data);
		
		JavaPairRDD<Integer,Integer> cartesianRDD = javaRDD.cartesian(javaRDD);
		System.out.println(cartesianRDD.collect());
		
		sc.stop();
	}
}

运行结果:

[(1,1), (1,2), (1,3), (1,4), 
(2,1), (2,2), (2,3), (2,4), 
(3,1), (3,2), (3,3), (3,4), 
(4,1), (4,2), (4,3), (4,4)]

13、fold的使用

官方文档描述:

Aggregate the elements of each partition, and then the results for all the partitions, 
using a given associative and commutative function and a neutral "zero value". 
The function op(t1, t2) is allowed to modify t1 and return it as its result value 
to avoid object allocation; however, it should not modify t2.

fold是aggregate的简化,将aggregate中的seqOp和combOp使用同一个函数op。

seqOp是对每个分区进行运算,combOp是各个分区结果进行运算,初始值都是zeroValue;
从源码中可以看出,先是将zeroValue赋值给jobResult,然后针对每个分区利用op函数与zeroValue进行计算,
再利用op函数将taskResult和jobResult合并计算,
同时更新jobResult,最后,将jobResult的结果返回。

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;

public class FoldDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("FoldDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<String> data = Arrays.asList("5", "1", "1", "3", "6", "2", "2");
		
		JavaRDD<String> javaRDD = sc.parallelize(data, 2);
		
		JavaRDD<String> partitionRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer v1, Iterator<String> v2) throws Exception {
				LinkedList<String> linkedList = new LinkedList<String>();
				
				while (v2.hasNext()) {
					linkedList.add(v1 + "=" + v2.next());
				}
				
				return linkedList.iterator();
			}
			// mapPartitionsWithIndex的第二个参数,表示是否保留父RDD的partitioner分区
		}, false);

		// [0=5, 0=1, 0=1, 1=3, 1=6, 1=2, 1=2]
		System.out.println(partitionRDD.collect());
		System.out.println("=========================================");
		
		// 第一步,对每个分区进行计算,每个分区的初始值为0
		// 分区0的结果为: 0-5-1-1
		// 分区1的结果为: 0-3-6-2-2
		// 第二步,对各个分区的结果进行计算,初始值为0
		// 最终结果为: 0-0-5-1-1-0-3-6-2-2
		String foldRDD = javaRDD.fold("0", new Function2<String, String, String>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public String call(String v1, String v2) throws Exception {
				System.out.println("v1=" + v1 + ", v2=" + v2 + ", v1+v2=" + (v1 + "-" + v2));
				return v1 + "-" + v2;
			}
		});
		
		// 0-0-5-1-1-0-3-6-2-2
		System.out.println(foldRDD);
		
		sc.stop();
	}
}

运行结果和案例解析:

// 分区0有3个数字,分别为:5,1,1
// 分区1有4个数字,分别为:3,6,2,2
[0=5, 0=1, 0=1, 1=3, 1=6, 1=2, 1=2]

// 分区0有3个数字(5,1,1),初始值 zeroValue 为0,
// 3个数字依次调用call函数进行运算,第一次传入的v1为zeroValue,
// 每次运算的结果作为下一次的v1,分区0的最终结果为:0-5-1-1
v1=0, v2=5, v1+v2=0-5
v1=0-5, v2=1, v1+v2=0-5-1
v1=0-5-1, v2=1, v1+v2=0-5-1-1

// 分区1有4个数字(3,6,2,2),初始值 zeroValue 为0,
// 4个数字依次调用call函数进行运算,第一次传入的v1为zeroValue,
// 每次运算的结果作为下一次的v1,分区1的最终结果为:0-3-6-2-2
v1=0, v2=3, v1+v2=0-3
v1=0-3, v2=6, v1+v2=0-3-6
v1=0-3-6, v2=2, v1+v2=0-3-6-2
v1=0-3-6-2, v2=2, v1+v2=0-3-6-2-2

// 分区0的结果为: 0-5-1-1
// 分区1的结果为: 0-3-6-2-2
// 各个分区的结果,依次调用call函数进行运算,初始值 zeroValue 为0
// 第一次传入的v1为zeroValue,每次运算的结果作为下一次的v1
v1=0, v2=0-5-1-1, v1+v2=0-0-5-1-1
v1=0-0-5-1-1, v2=0-3-6-2-2, v1+v2=0-0-5-1-1-0-3-6-2-2

14、countByKey的使用

官方文档描述:

Count the number of elements for each key, collecting the results to a local Map. Note that this method should only be used if the resulting map is expected to be small, as the whole thing is loaded into the driver's memory. To handle very large results, consider using rdd.mapValues(_ => 1L).reduceByKey(_ + _), which returns an RDD[T, Long] instead of a map.

源码分析:

def countByKey(): Map[K, Long] = self.withScope {  
   self.mapValues(_ => 1L).reduceByKey(_ + _).collect().toMap
}

从源码中可以看出,先是进行map操作转化为(key,1)键值对,再进行reduce聚合操作,最后利用collect函数将数据加载到driver,并转化为map类型。
注意,从上述分析可以看出,countByKey操作将数据全部加载到driver端的内存,如果数据量比较大,可能出现OOM。因此,如果key数量比较多,建议进行rdd.mapValues(_ => 1L).reduceByKey(_ + _),返回RDD[T, Long]

实例:

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class CountByKeyDemo {
	
	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("CountByKeyDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<String> data = Arrays.asList("5", "1", "1", "3", "6", "2", "2");
		
		JavaRDD<String> javaRDD = sc.parallelize(data, 5);
		
		JavaRDD<String> partitionRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<String>, Iterator<String>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer v1, Iterator<String> v2) throws Exception {
				
				LinkedList<String> linkedList = new LinkedList<String>();
				
				while (v2.hasNext()) {
					linkedList.add(v1 + "=" + v2.next());
				}
				
				return linkedList.iterator();
			}
			
		}, false);
		
		// [0=5, 1=1, 2=1, 2=3, 3=6, 4=2, 4=2]
		System.out.println(partitionRDD.collect());
		
		JavaPairRDD<String, String> javaPairRDD = javaRDD.mapToPair(new PairFunction<String, String, String>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Tuple2<String, String> call(String s) throws Exception {
				return new Tuple2<String, String>(s, s);
			}
			
		});
		
		// {5=1, 6=1, 1=2, 2=2, 3=1}
		System.out.println(javaPairRDD.countByKey());
		
		sc.stop();
	}
}

15、reduce的使用

官方文档描述:

Reduces the elements of this RDD using the specified commutative and associative binary operator.

函数原型:

def reduce(f: JFunction2[T, T, T]): T

根据映射函数f,对RDD中的元素进行二元计算(满足交换律和结合律),返回计算结果。

从源码中可以看出,reduce函数相当于对RDD中的元素进行reduceLeft函数操作,reduceLeft函数是从列表的左边往右边应用reduce函数;之后,在driver端对结果进行合并处理,因此,如果分区数量过多或者自定义函数过于复杂,对driver端的负载比较重。

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;

public class ReduceDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("ReduceDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 3);
		
		Integer result = javaRDD.reduce(new Function2<Integer, Integer, Integer>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Integer call(Integer v1, Integer v2) throws Exception {
				return v1 + v2;
			}
		});
		
		// 19
		System.out.println(result);
		
		sc.stop();
	}
}

16、aggregate的使用

官方文档描述:

Aggregate the elements of each partition, and then the results for all the partitions, using given combine functions and a neutral "zero value".
 This function can return a different result type, U, than the type of this RDD, T. Thus, we need one operation for merging a T into an U and one operation for merging two U's, 
as in scala.TraversableOnce. Both of these functions are allowed to modify and return their first argument instead of creating a new U to avoid memory allocation.

aggregate函数将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。 这个函数最终返回U的类型不需要和RDD的T中元素类型一致。 这样,我们需要一个函数将T中元素合并到U中,另一个函数将两个U进行合并。 其中,参数1是初值元素;参数2是seq函数是与初值进行比较;参数3是comb函数是进行合并。

注意:如果没有指定分区,aggregate是计算每个分区的,空值则用初始值替换。

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;

public class AggregateDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("AggregateDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data,3);
		
		JavaRDD<String> partitionRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<Integer>, Iterator<String>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer v1, Iterator<Integer> v2) throws Exception {
				LinkedList<String> linkedList = new LinkedList<String>();
				
				while (v2.hasNext()) {
					linkedList.add(v1 + "=" + v2.next());
				}
				
				return linkedList.iterator();
			}
			// mapPartitionsWithIndex的第二个参数,表示是否保留父RDD的partitioner分区
		}, false);
		
		// [0=5, 0=1, 1=1, 1=4, 2=4, 2=2, 2=2]
		System.out.println(partitionRDD.collect());
		
		Integer aggregateValue = javaRDD.aggregate(3, new Function2<Integer, Integer, Integer>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Integer call(Integer v1, Integer v2) throws Exception {
			    System.out.println("v1=" + v1 + ", v2=" + v2 + ", max=" + Math.max(v1, v2));
			    return Math.max(v1, v2);
			  }
			}, new Function2<Integer, Integer, Integer>() {
				
				private static final long serialVersionUID = 1L;
				
				int i = 0;
				
				@Override
				public Integer call(Integer v1, Integer v2) throws Exception {
				    System.out.println("comb, i=" + (i++) + ", v1=" + v1 + ", v2=" + v2 + ", v1+v2=" + (v1+v2));
				    return v1 + v2;
				  }
				});
		
		System.out.println("aggregateValue=" + aggregateValue);
		
		sc.stop();
	}
}

运行结果:

// 有3个分区
// 分区0有2个数字: 5,1
// 分区1有2个数字: 1,4
// 分区2有3个数字: 4,2,2
[0=5, 0=1, 1=1, 1=4, 2=4, 2=2, 2=2]

// 第一步:每个分区的运算
// 每个分区,初始值 zeroValue 为3,每个数字依次调用第一个函数(称为seq函数),
// 与zeroValue进行比较,找到最大值

// 分区0的最大值为5
v1=3, v2=5, max=5
v1=5, v2=1, max=5

// 分区1的最大值为4
v1=3, v2=1, max=3
v1=3, v2=4, max=4

// 分区2的最大值为4
v1=3, v2=4, max=4
v1=4, v2=2, max=4
v1=4, v2=2, max=4

// 第二步:分区结果的运算
// 初始值 zeroValue 为3,对各个分区的结果,依次调用第二个函数(称为comb函数)
// 第二个函数是相加,zeroValue和各个分区的结果相加:3+5+4+4
comb, i=0, v1=3, v2=5, v1+v2=8
comb, i=1, v1=8, v2=4, v1+v2=12
comb, i=2, v1=12, v2=4, v1+v2=16

// 最终结果
aggregateValue=16

17、aggregateByKey的使用

官方文档描述:

Aggregate the values of each key, using given combine functions and a neutral "zero value".This function can return a different result type, U
, than the type of the values in this RDD,V.Thus, we need one operation for merging a V into a U and one operation for merging two U's,as in scala.TraversableOnce.
The former operation is used for merging values within a partition, and the latter is used for merging values between partitions. To avoid memory allocation,
both of these functions are allowed to modify and return their first argument instead of creating a new U.

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

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class AggregateByKeyDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("AggregateByKeyDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		int numPartitions = 4;
		JavaRDD<Integer> javaRDD = sc.parallelize(data);
		final Random random = new Random(100);
		
		JavaPairRDD<Integer,Integer> javaPairRDD = javaRDD.mapToPair(new PairFunction<Integer, Integer, Integer>() {
		  @Override
		  public Tuple2<Integer, Integer> call(Integer integer) throws Exception {
		    return new Tuple2<Integer, Integer>(integer,random.nextInt(10));
		  }
		});
		
		System.out.println(javaPairRDD.collect());

		JavaPairRDD<Integer, Integer> aggregateByKeyRDD = javaPairRDD.aggregateByKey(3, numPartitions, new Function2<Integer, Integer, Integer>() {
			@Override
			public Integer call(Integer v1, Integer v2) throws Exception {
				System.out.println("v1=" + v1 + ", v2=" + v2 + ", max=" + Math.max(v1, v2));
				return Math.max(v1, v2);
			}
		}, new Function2<Integer, Integer, Integer>() {
			
			private static final long serialVersionUID = 1L;
			
			int i = 0;
			@Override
			public Integer call(Integer v1, Integer v2) throws Exception {
				System.out.println("comb, i=" + (i++) + ", v1=" + v1 + ", v2=" + v2 + ", v1+v2=" + (v1+v2));
				return v1 + v2;
			}
		});
		
		System.out.println("aggregateByKeyRDD.partitions().size() = "+aggregateByKeyRDD.partitions().size());
		System.out.println("aggregateByKeyRDD = " + aggregateByKeyRDD.collect());
		
		sc.stop();
	}
}

运行结果:

// javapair的key和value
[(5,5), (1,0), (1,4), (4,8), (4,1), (2,6), (2,6)]

// 每个key的所有value,和初始值zeroValue进行比较,初始值设置为3

// 第一个key的比较结果是5
v1=3, v2=5, max=5

// 第二个key的比较结果是4
v1=3, v2=0, max=3
v1=3, v2=4, max=4

// 第三个key的比较结果是8
v1=3, v2=8, max=8
v1=8, v2=1, max=8

// 第四个key的比较结果是6
v1=3, v2=6, max=6
v1=6, v2=6, max=6

// aggregateByKey之后的结果,找到每个key对应的value的最大值
aggregateByKeyRDD = [(4,8), (1,4), (5,5), (2,6)]

// aggregateByKey之后的结果,有4个分区
aggregateByKeyRDD.partitions().size() = 4

18、foreach的使用

官方文档描述:

Applies a function f to all elements of this RDD.foreach用于遍历RDD,将函数f应用于每一个元素。
实例:
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;

public class ForeachDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("ForeachDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 3);
		
		javaRDD.foreach(new VoidFunction<Integer>() {
			
			private static final long serialVersionUID = 1L;

			@Override
			public void call(Integer value) throws Exception {
				System.out.println(value);
			}
		});
		
		sc.stop();
	}
}

19、foreachPartition的使用

官方文档描述:

Applies a function f to each partition of this RDD.foreachPartition和foreach类似,只不过是对每一个分区使用f。
实例:
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.VoidFunction;

public class ForeachPartitionDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("ForeachPartitionDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);

		JavaRDD<Integer> javaRDD = sc.parallelize(data, 3);

		//获得分区ID
		JavaRDD<String> partitionRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<Integer>, Iterator<String>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer v1, Iterator<Integer> v2) throws Exception {
				LinkedList<String> linkedList = new LinkedList<String>();
				while(v2.hasNext()) {
					linkedList.add(v1 + "=" + v2.next());
				}
				return linkedList.iterator();
			}
		},  false);
		
		// [0=5, 0=1, 1=1, 1=4, 2=4, 2=2, 2=2]
		System.out.println(partitionRDD.collect());
		
		javaRDD.foreachPartition(new VoidFunction<Iterator<Integer>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public void call(Iterator<Integer> integerIterator) throws Exception {
				System.out.println("___________begin_______________");
				while (integerIterator.hasNext()) {
					System.out.print(integerIterator.next() + "    ");
				}
				System.out.println("\n___________end_______________");
			}
		});
		
		sc.stop();
	}
}

运行结果:

// 分区0的数字:5,1
// 分区1的数字:1,4
// 分区2的数字:4,2,2
[0=5, 0=1, 1=1, 1=4, 2=4, 2=2, 2=2]


// 分区0的数字:5,1
___________begin_______________
5    1    
___________end_______________


// 分区1的数字:1,4
___________begin_______________
1    4    
___________end_______________


// 分区2的数字:4,2,2
___________begin_______________
4    2    2    
___________end_______________

20、lookup的使用

官方文档描述:

Return the list of values in the RDD for key `key`. This operation is done efficiently if 
the RDD has a known partitioner by only searching the partition that the key maps to.
lookup用于(K,V)类型的RDD,指定K值,返回RDD中该K对应的所有V值。
从源码中可以看出,如果partitioner不为空,计算key得到对应的partition,在从该partition中获得key对应的所有value;
如果partitioner为空,则通过filter过滤掉其他不等于key的值,然后将其value输出。
实例:
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;

import scala.Tuple2;

public class LookupDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("LookupDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 3);
		
		JavaPairRDD<Integer,Integer> javaPairRDD = javaRDD.mapToPair(new PairFunction<Integer, Integer, Integer>() {
		  
			private static final long serialVersionUID = 1L;
			int i = 0;
			@Override
			public Tuple2<Integer, Integer> call(Integer integer) throws Exception {
				i++;
				return new Tuple2<Integer, Integer>(integer, integer + 3);
			}
		});
		
		System.out.println(javaPairRDD.collect());
		
		System.out.println("lookup------------" + javaPairRDD.lookup(4));
		
		sc.stop();
	}
}

运行结果:

[(5,8), (1,4), (1,4), (4,7), (4,7), (2,5), (2,5)]


lookup------------[7, 7]

21、saveAsTextFile的使用

官方文档描述:

Save this RDD as a text file, using string representations of elements.
saveAsTextFile用于将RDD以文本文件的格式存储到文件系统中。
从源码中可以看到,saveAsTextFile函数是依赖于saveAsHadoopFile函数,由于saveAsHadoopFile函数接受PairRDD,
所以在saveAsTextFile函数中利用rddToPairRDDFunctions函数转化为(NullWritable,Text)类型的RDD,然后通过saveAsHadoopFile函数实现相应的写操作。
实例:
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

public class SaveAsTextFileDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("LookupDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data,5);
		
		javaRDD.saveAsTextFile("/Users/silva/Downloads/testText");
		
		sc.stop();
	}
}

22、saveAsObjectFile的使用

官方文档描述:

Save this RDD as a SequenceFile of serialized objects.saveAsObjectFile用于将RDD中的元素序列化成对象,存储到文件中。
从源码中可以看出,saveAsObjectFile函数是依赖于saveAsSequenceFile函数实现的,将RDD转化为类型为
实例:
package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

public class SaveAsObjectFileDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("LookupDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data,5);
		
		javaRDD.saveAsObjectFile("/Users/silva/Downloads/testObject");
		
		sc.stop();
	}
}

23、treeAggregate的使用

官方文档描述:

Aggregates the elements of this RDD in a multi-level tree pattern.从源码中可以看出,treeAggregate函数先是对每个分区利用scala的aggregate函数进行局部聚合的操作;同时,依据depth参数计算scale,如果当分区数量过多时,则按i%curNumPartitions进行key值计算,再按key进行重新分区合并计算;最后,在进行reduce聚合操作。这样可以通过调解深度来减少reduce的开销。
List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
JavaRDD<Integer> javaRDD = javaSparkContext.parallelize(data,3);
//转化操作
JavaRDD<String> javaRDD1 = javaRDD.map(new Function<Integer, String>() {
  @Override
  public String call(Integer v1) throws Exception {
    return Integer.toString(v1);
  }
});

String result1 = javaRDD1.treeAggregate("0", new Function2<String, String, String>() {
  @Override
  public String call(String v1, String v2) throws Exception {
    System.out.println(v1 + "=seq=" + v2);
    return v1 + "=seq=" + v2;
  }
}, new Function2<String, String, String>() {
    @Override
    public String call(String v1, String v2) throws Exception {
      System.out.println(v1 + "<=comb=>" + v2);
      return v1 + "<=comb=>" + v2;
  }
});
System.out.println(result1);

24、treeReduce的使用

官方文档描述:

Reduces the elements of this RDD in a multi-level tree pattern.与treeAggregate类似,只不过是seqOp和combOp相同的treeAggregate。
从源码中可以看出,treeReduce函数先是针对每个分区利用scala的reduceLeft函数进行计算;最后,在将局部合并的RDD进行treeAggregate计算,这里的seqOp和combOp一样,初值为空。在实际应用中,可以用treeReduce来代替reduce,
主要是用于单个reduce操作开销比较大,而treeReduce可以通过调整深度来控制每次reduce的规模。
实例:
List<Integer> data = Arrays.asList(5, 1, 1, 4, 4, 2, 2);
JavaRDD<Integer> javaRDD = javaSparkContext.parallelize(data,5);
JavaRDD<String> javaRDD1 = javaRDD.map(new Function<Integer, String>() {
    @Override
    public String call(Integer v1) throws Exception {
      return Integer.toString(v1);
    }
});
String result = javaRDD1.treeReduce(new Function2<String, String, String>() {
    @Override
    public String call(String v1, String v2) throws Exception {
      System.out.println(v1 + "=" + v2);
      return v1 + "=" + v2;
  }
});
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + treeReduceRDD);

25、combineByKey的使用

官方文档描述:

Generic function to combine the elements for each key using a custom set of aggregation functions. Turns an RDD[(K, V)] into a result of type RDD[(K, C)], for a "combined type" C Note that V and C can be different -- for example, one might group an RDD of type (Int, Int) into an RDD of type (Int, Seq[Int]). 

tions:
 - `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.
In addition, users can control the partitioning of the output RDD, and whether to perform map-side aggregation (if a mapper can produce multiple items with the same key).
该函数是用于将RDD[k,v]转化为RDD[k,c],其中类型v和类型c可以相同也可以不同。 
其中的参数如下: 
- createCombiner:该函数用于将输入参数RDD[k,v]的类型V转化为输出参数RDD[k,c]中类型C; 
- mergeValue:合并函数,用于将输入中的类型C的值和类型V的值进行合并,得到类型C,输入参数是(C,V),输出是C; 
- mergeCombiners:合并函数,用于将两个类型C的值合并成一个类型C,输入参数是(C,C),输出是C; 
- numPartitions:默认HashPartitioner中partition的个数; 
- partitioner:分区函数,默认是HashPartitionner; 
- mapSideCombine:该函数用于判断是否需要在map进行combine操作,类似于MapReduce中的combine,默认是 true。

从源码中可以看出,combineByKey()的实现是一边进行aggregate,一边进行compute() 的基础操作。假设一组具有相同 K 的 <K, V> records 正在一个个流向 combineByKey(),createCombiner 将第一个 record 的 value 初始化为 c (比如,c = value),然后从第二个 record 开始,来一个 record 就使用 mergeValue(c, record.value) 来更新 c,比如想要对这些 records 的所有 values 做 sum,那么使用c = c + record.value。等到 records 全部被 mergeValue(),得到结果 c。假设还有一组 records(key 与前面那组的 key 均相同)一个个到来,combineByKey() 使用前面的方法不断计算得到 c’。现在如果要求这两组 records 总的 combineByKey() 后的结果,那么可以使用 final c = mergeCombiners(c, c') 来计算;然后依据partitioner进行不同分区合并。

实例:

package com.spark.api.spark_java_api_learn;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class CombineByKeyDemo {

	public static void main(String[] args) {
		
		SparkConf conf = new SparkConf()
				.setMaster("local")
				.setAppName("LookupDemo");
		
		JavaSparkContext sc = new JavaSparkContext(conf);
		
		List<Integer> data = Arrays.asList(1, 2, 4, 3, 5, 6, 7, 1, 2, 2, 2, 2);
		
		JavaRDD<Integer> javaRDD = sc.parallelize(data, 2);
		
		// 看每个元素的分区
		JavaRDD<String> partitionRDD = javaRDD.mapPartitionsWithIndex(new Function2<Integer, Iterator<Integer>, Iterator<String>>() {

			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(Integer v1, Iterator<Integer> v2) throws Exception {
				LinkedList<String> linkedList = new LinkedList<String>();
				
				while (v2.hasNext()) {
					linkedList.add(v1 + "=" + v2.next());
				}
				
				return linkedList.iterator();
			}
			// mapPartitionsWithIndex的第二个参数,表示是否保留父RDD的partitioner分区
		}, false);
		
		// [0=1, 0=2, 0=4, 0=3, 0=5, 0=6, 1=7, 1=1, 1=2, 1=2, 1=2, 1=2]
		System.out.println(partitionRDD.collect());
		
		
		//转化为pairRDD
		JavaPairRDD<Integer,Integer> javaPairRDD = javaRDD.mapToPair(new PairFunction<Integer, Integer, Integer>() {
		    
			private static final long serialVersionUID = 1L;

			@Override
		    public Tuple2<Integer, Integer> call(Integer integer) throws Exception {
		    return new Tuple2<Integer, Integer>(integer, integer);
		  }
		});
		
		// [(1,1), (2,2), (4,4), (3,3), (5,5), (6,6), (7,7), (1,1), (2,2), (2,2), (2,2), (2,2)]
		System.out.println(javaPairRDD.collect());
		
		JavaPairRDD<Integer,String> combineByKeyRDD = javaPairRDD.combineByKey(new Function<Integer, String>() {
		    @Override
		    public String call(Integer v1) throws Exception {
		    	System.out.println(v1 + " :createCombiner: ");
		    	return v1 + " :createCombiner: ";
		    }
		  }, new Function2<String, Integer, String>() {
			  @Override
			  public String call(String v1, Integer v2) throws Exception {
				  System.out.println(v1 + " :mergeValue: " + v2);
		      return v1 + " :mergeValue: " + v2;
			  }
		  }, new Function2<String, String, String>() {
			  	@Override
			  	public String call(String v1, String v2) throws Exception {
			  		System.out.println(v1 + " :mergeCombiners: " + v2);
			  		return v1 + " :mergeCombiners: " + v2;
			 }
		});
		
		System.out.println("result~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + combineByKeyRDD.collect());
		
		sc.stop();
	}
}

运行结果和案例分析:

// 分区0有6个元素:1,2,4,3,5,6
// 分区1有6个元素:7,1,2,2,2,2
[0=1, 0=2, 0=4, 0=3, 0=5, 0=6, 1=7, 1=1, 1=2, 1=2, 1=2, 1=2]

// 把元素转化为Pair
[(1,1), (2,2), (4,4), (3,3), (5,5), (6,6), (7,7), (1,1), (2,2), (2,2), (2,2), (2,2)]

// 分区0的数字,key没有重复,所以每个value只进入到createCombiner函数
1 :createCombiner: 
2 :createCombiner: 
4 :createCombiner: 
3 :createCombiner: 
5 :createCombiner: 
6 :createCombiner: 

// 分区1的数字,key有些是重复,重复的会进入到mergeValue函数
7 :createCombiner: 
1 :createCombiner: 
2 :createCombiner: 
2 :createCombiner:  :mergeValue: 2
2 :createCombiner:  :mergeValue: 2 :mergeValue: 2
2 :createCombiner:  :mergeValue: 2 :mergeValue: 2 :mergeValue: 2

// 2个分区都有的,要进行合并,也就是进入到mergeCombiners函数
1 :createCombiner:  :mergeCombiners: 1 :createCombiner: 
2 :createCombiner:  :mergeCombiners: 2 :createCombiner:  :mergeValue: 2 :mergeValue: 2 :mergeValue: 2

最终的输出结果
result~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[(4,4 :createCombiner: ), 
(6,6 :createCombiner: ), 
(2,2 :createCombiner:  :mergeCombiners: 2 :createCombiner:  :mergeValue: 2 :mergeValue: 2 :mergeValue: 2), 
(1,1 :createCombiner:  :mergeCombiners: 1 :createCombiner: ), 
(3,3 :createCombiner: ), 
(7,7 :createCombiner: ), 
(5,5 :createCombiner: )]

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值