Spark本地运行的几个实例代码(Java实现)

Spark本地运行的几个实例代码(Java实现)

初学spark,用Java写了几个本地运行的spark实例代码,来记录一下已学的spark常用的算子的使用和处理逻辑,不涉及分布式集群。相关内容仅为自己的个人理解,如有错误还请指出。

实例一:词频数统计

问题描述

统计一个文本文件中的每个单词的出现次数,数据格式:
words.txt

过程分析

首先通过textFile()函数将文件读入JavaRDD,然后通过flatMap算子将每一行的数据进行分割,得到多个String,一行数据分割得到的多个String以Iterator的迭代器格式返回,返回之后的Iterator中的每一个String都会作为一个RDD。接着通过mapToPair算子给每一个word添加计数标记1(代表出现1次),该算子返回一个键值对RDD。最后通过reduceByKey算子根据相同的key对RDD进行reduce聚合操作,进行统计计数。

代码

public class SparkWordCount {
    public static void main(String[] args){
        SparkConf conf = new SparkConf();
        //添加这一行则在本地运行,不添加这一行则默认在集群执行
        conf.setMaster("local");
        conf.setAppName("WordCount");
        //基本的初始化
        JavaSparkContext sc=new JavaSparkContext(conf);
        //创建String类型的RDD,并从本地文件中读取数据
        JavaRDD<String> fileRDD = sc.textFile("src/main/files/words.txt");//通过文件读入创建RDD

        //flatMap()算子用来分割操作,将原RDD中的数据分成一个个片段
        //new FlatMapFunction<String, String>中的两个String分别表示输入和输出类型
        JavaRDD<String> wordRDD = fileRDD.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String line) throws Exception {
            	//通过Iterator迭代器可以将分割后的多个数据元素全部返回输出
                return Arrays.asList(line.split("\\s+")).iterator();
            }
        });

        //mapToPair()算子是用来对分割后的一个个片段结果添加计数标志的,如出现次数1,该函数用来创建并返回pair类型的RDD. new PairFunction<String, String, Integer>中分别是输入类型String和输出类型<String, Integer>.
        JavaPairRDD<String, Integer> wordOneRDD = wordRDD.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String word) throws Exception {
                return new Tuple2<>(word, 1); //Tuple2是spark的二元数组类型,Java中没有
            }
        });

        //reduceByKey()算子是根据key来聚合,reduce阶段.new Function2<Integer, Integer, Integer>中分别是用来聚合的两个输入类型Integer,Integer和聚合后的输出类型Integer.
        JavaPairRDD<String, Integer> wordCountRDD = wordOneRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer i1, Integer i2) throws Exception {
                return i1 + i2;
            }
        });

        wordCountRDD.saveAsTextFile("E:\\result7");
    }

}

运行结果

在这里插入图片描述

实例二:统计平均年龄

问题描述

java实现spark统计100万人口的平均年龄以及每个年龄的出现次数,数据格式为“序号 年龄”
peopleAges.txt
数据生成代码:

//生成年龄数据,格式“序号  年龄”
    private static void makeAgeData() throws IOException {
        File newFile = new File("src/main/files/peopleAges.txt");
        if (newFile.exists()){
            System.out.println("文件已存在!");
            return;
        }
        newFile.createNewFile();
        FileWriter fw = new FileWriter(newFile,true);
        Random rand = new Random();
        for (int i=1;i<=1000000;i++){
            fw.append(i+"  "+(rand.nextInt(100)+1)+"\n");
            fw.flush();
        }
        fw.close();
    }

过程分析

首先通过textFile()函数将文件数据读入RDD中。然后使用mapToPair()算子将每一行数据中的年龄作为key并对每一个年龄添加计数标记1作为value,接着使用reduceByKey算子对相同年龄值的数据进行聚合。
求平均年龄时首先要求出年龄和,这也是reduce聚合操作。但是要注意reduce算子只能接收单个数据元素组成的RDD作为输入,不能接收pair类型的RDD,所以对源文件读出的RDD先通过map算子输出只有年龄值数据的RDD,然后进行reduce()。

代码

public class AvgAge {
    public static void main(String[] args){
        SparkConf conf = new SparkConf();
        conf.setMaster("local");
        conf.setAppName("AvgAge");
        JavaSparkContext sc = new JavaSparkContext(conf);
        //刚从文件读出来的RDD已经是一行一行的字符串,所以可以直接进行mapToPair
        JavaRDD<String> fileRDD = sc.textFile("src/main/files/peopleAges.txt");
        JavaPairRDD<Integer, Integer> ageOneRdd = fileRDD.mapToPair(new PairFunction<String, Integer, Integer>() {
            @Override
            public Tuple2<Integer, Integer> call(String s) throws Exception {
                return new Tuple2<>(Integer.parseInt(s.split("\\s+")[1]),1);
            }
        });

		//使用reduceByKey算子对具有相同年龄值的数据进行聚合,获取每个年龄值的人数
        JavaPairRDD<Integer, Integer> ageCountRDD = ageOneRdd.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) throws Exception {
                return integer+integer2;
            }
        });

        //求平均年龄
        //先通过map算子取出每个年龄值作为一个RDD。
        //reduce()函数的输入RDD不能是pair,只能是单个数据组成的RDD
        Integer ageSum = fileRDD.map(new Function<String, Integer>() {
            @Override
            public Integer call(String s) throws Exception {
                return Integer.parseInt(s.split("\\s+")[1]);
            }
        }).reduce(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer s, Integer s2) throws Exception {
                return s+s2;
            }
        });
        System.out.println("平均年龄:"+ageSum/fileRDD.count());

        ageCountRDD.saveAsTextFile("src/main/files/ageAnalysis");

    }

运行结果

在这里插入图片描述

案例三:统计身高最值

问题描述

统计男女人数,并分别计算出男性和女性的最高和最低身高,数据格式“序号 M/F 身高”
在这里插入图片描述
数据生成代码

	//生成性别身高数据,格式“序号  性别(M/F) 身高”
    private static void makeHeightData() throws IOException {
        File newFile = new File("src/main/files/heightData.txt");
        if (newFile.exists()){
            System.out.println("文件已存在!");
            return;
        }
        newFile.createNewFile();
        FileWriter fw = new FileWriter(newFile,true);
        Random rand = new Random();
        for (int i=1;i<=50000;i++){
            fw.append(i+"  M  "+(rand.nextInt(100)+100)+"\n");
            fw.append(i+"  F  "+(rand.nextInt(80)+100)+"\n");
            fw.flush();
        }
        fw.close();
    }

过程分析

首先通过textFile()函数将文件数据读入RDD。然后使用filter算子分别过滤出男性和女性数据,接着用map算子分割出身高值并将其转化成Integer类型,这样才能用于数字排序,然后使用sortBy算子排序。sortBy算子可以直接对RDD中的数据排序,不用区分key还是value。

代码

public class HeightMaxMin {
    public static void main(String[] args){
        SparkConf conf = new SparkConf();
        conf.setMaster("local");
        conf.setAppName("HeightAnalysis");
        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD<String> fileRDD = sc.textFile("src/main/files/heightData.txt");
        //使用filter算子分别过滤出男性和女性数据
        JavaRDD<String> maleRDD = fileRDD.filter(new Function<String, Boolean>() {
            @Override
            //如果这行数据符合过滤条件则返回true
            public Boolean call(String s) throws Exception {
                return s.contains("M");
            }
        });
        JavaRDD<String> femaleRDD = fileRDD.filter(new Function<String, Boolean>() {
            @Override
            //如果这行数据符合过滤条件则返回true
            public Boolean call(String s) throws Exception {
                return s.contains("F");
            }
        });

        //使用map算子分割出身高并转化为整数类型,这样才能用排序
        JavaRDD<Integer> maleHeightRDD = maleRDD.map(new Function<String, Integer>() {
            @Override
            public Integer call(String s) throws Exception {
                return Integer.parseInt(s.split("\\s+")[2]);
            }
        });
        JavaRDD<Integer> femaleHeightRDD = femaleRDD.map(new Function<String, Integer>() {
            @Override
            public Integer call(String s) throws Exception {
                return Integer.parseInt(s.split("\\s+")[2]);
            }
        });

        //使用sortBy算子排序
        JavaRDD<Integer> sortmaleHeightRDD = (JavaRDD<Integer>) maleHeightRDD.sortBy(new Function<Integer, Object>() {
            @Override
            //返回的是排序的内容,即对输入RDD中的哪一部分进行排序就输出哪一部分
            public Object call(Integer integer) throws Exception {
                return integer;
            }
        }, false,10);//false降序true升序,10是partition分区数,因为没有用集群所以也不太明白这个分区数具体指什么
        JavaRDD<Integer> sortfemaleHeightRDD = (JavaRDD<Integer>) femaleHeightRDD.sortBy(new Function<Integer, Object>() {
            @Override
            public Object call(Integer integer) throws Exception {
                return integer;
            }
        }, false,10);//第二个参数true/false是正序逆序,最后一个参数10是分区数

		//first()函数返回排名第一的数据
        System.out.println("男性: "+sortmaleHeightRDD.count()+"  "+sortmaleHeightRDD.first());
        System.out.println("女性: "+sortfemaleHeightRDD.count()+"  "+sortfemaleHeightRDD.first());

    }
}

运行结果

在这里插入图片描述
在这里插入图片描述

案例四:统计单词频率

问题描述

统计一段文本里出现频率最高的前k个词,注意单词不分大小写。

过程分析

首先从文件读入数据到RDD,然后使用flatMap算子对每一行的数据按照空格进行分割,并将所有的字母都转为小写,接着使用mapToPair算子对每一个单词添加计数标记1,然后使用reduceByKey算子对单词进行reduce聚合,为了根据key来排序,聚合后再使用mapToPair算子将得到的pair里面的key和value调换一下位置。然后使用sortByKey算子根据key来进行排序,最后使用take算子取出排名前5的数据。

代码

public class wordTopK {
    public static void main(String[] args){
        SparkConf conf = new SparkConf();
        conf.setMaster("local");
        conf.setAppName("wordTopK");
        JavaSparkContext sc=new JavaSparkContext(conf);

        JavaRDD<String> fileRDD = sc.textFile("D:\\summer_study\\ppt\\hive.txt");
        //使用flatmap算子对每一行数据按空格分隔,并将所有的字母都转为小写
        //注意这里不用map是因为map只能输出一个数据元素,而flatMap可以在输入元素后添加任意多元素来输出,
        // 比如分割后的多个元素组成Iterator来输出,但是Iterator里的每一个元素依然是独立的RDD。
        JavaRDD<String> wordRDD = fileRDD.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String line) throws Exception {
                return Arrays.asList(line.toLowerCase().split("\\s+")).iterator();
            }
        });

        //使用maoToPair算子给每一个word加上计数标记1
        JavaPairRDD<String, Integer> wordOneRDD = wordRDD.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s,1);
            }
        });

        //使用reduceByKey算子对word进行reduce聚合,为了根据key来排序,聚合后再将得到的pair里面的key和value调换一下
        JavaPairRDD<Integer, String> wordCountRDD = wordOneRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) throws Exception {
                return integer+integer2;
            }
        }).mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {
            @Override
            public Tuple2<Integer, String> call(Tuple2<String, Integer> stringIntegerTuple2) throws Exception {
                return new Tuple2<>(stringIntegerTuple2._2,stringIntegerTuple2._1);
            }
        });

        //使用sortByKey算子排序,false降序,true升序
        JavaPairRDD<Integer,String> sortWordCountRDD = wordCountRDD.sortByKey(false);

        //使用take算子取出前5
        List<Tuple2<Integer,String>> result = sortWordCountRDD.take(5);
        System.out.println("结果:"+result.toString());
    }
}

运行结果

在这里插入图片描述

一些总结:

  1. RDD就是spark中专用的一种数据格式,代表一种抽象数据类型,spark中的数据都是存在不同类型的RDD中,如JavaRDD<String>,JavaPairRDD<String,Integer>等。
  2. textFile读文件生成的RDD可以理解成源文件中一行一行的数据,每一行的数据就是一个JavaRDD.
  3. flatMap算子和map算子的区别:map算子就是将源JavaRDD的一个一个元素的传入call方法,并经过算法后一个一个的返回从而生成一个新的JavaRDD,注意call返回的数据只能是单个数据元素。 flatMap与map一样,是将RDD中的元素依次的传入call方法,他比map多的功能是能在任何一个传入call方法的元素后面添加任意多元素,而能达到这一点,正是因为其进行传参是依次进行的。比如分割后的多个元素组成Iterator来输出,但是Iterator里的每一个元素依然是独立的RDD,这个Iterator只能由flatMap算子输出,map算子不可以,因为它只能输出单个数据元素。

运行环境

ide:Idea
jdk:1.8.0_121
spark:2.1.1
附maven配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ffxn</groupId>
    <artifactId>ffxn</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>2.1.1</version>
            <!--<scope>provided</scope>-->
        </dependency>

    </dependencies>

</project>

注:以上仅为个人学习记录,spark初学菜鸟,如有错误,敬请提出。
参考:
Spark 入门实战之最好的实例
Spark学习之路

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值