题目:

有一个很大的文件,这文件中的内容全部都是数字,要求尝试从这个文件中找出最大的10个数字。

分析:

看起来像是一个比较简单的问题。不用大数据框架的话,也能比较轻易的实现:就是逐个读取文件中的每个数字,放到一个大顶堆结构中;将大顶堆放满以后,每读取一个数字就将之和大顶堆中的最小值进行比较,如果其大于这个最小值的话,就将其放入堆中,并将堆中的最小值删除;这样读取到最后,堆中剩下来的内容就是top 10了。

用MapReduce实现的话也说不上困难:我们只使用Map任务读取文件,而reduce中输出的内容就是一个有序的结果集,那么后十位自然就是Top10了。这方案虽说可行,但绝说不上是好的方案。

换个思路:map任务中先完成一轮过滤(没必要多添一重Combiner),先取出每个Map中的top10来,而后在reduce中再进行一轮筛选,从所有map的top10中再选出个top10来。这样处理效率应该会高一些。

看看实现过程:

package com.zhyea.dev;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.IOException;
import java.util.TreeSet; public class TopN { private static final Logger logger = LoggerFactory.getLogger(TopN.class); public static class SplitterMapper extends Mapper<Object, Text, IntWritable, NullWritable> { private static final IntWritable intWritable = new IntWritable(); private static final TreeSet<Integer> set = new TreeSet<>(); @Override
public void map(Object key, Text value, Context context) {
int num = Integer.valueOf(value.toString()); if (set.size() < 10) {
set.add(num);
return;
} if (num > set.first()) {
set.add(num);
set.pollFirst();
}
} @Override
public void cleanup(Context context) {
for (Integer i : set) {
intWritable.set(i);
try {
context.write(intWritable, NullWritable.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
} public static class IntegrateReducer extends Reducer<IntWritable, NullWritable, IntWritable, NullWritable> { private static final IntWritable intWritable = new IntWritable();
private static final TreeSet<Integer> set = new TreeSet<>(); @Override
public void reduce(IntWritable key, Iterable<NullWritable> values, Context context) {
try {
int num = key.get();
if (set.size() < 10) {
set.add(num);
return;
} if (num > set.first()) {
set.add(num);
set.pollFirst();
}
} catch (Exception e) {
e.printStackTrace();
}
} @Override
public void cleanup(Context context) {
for (Integer i : set) {
intWritable.set(i);
try {
context.write(intWritable, NullWritable.get());
} catch (Exception e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "top-n");
job.setJarByClass(TopN.class); job.setMapperClass(SplitterMapper.class);
job.setReducerClass(IntegrateReducer.class); job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(NullWritable.class); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1);
} }

程序里在map或reduce方法中没有做任何输出,只是实现了比较逻辑,真正的输出是在cleanup方法中完成的。

用spark实现的话可以先做全排序,然后排重,take前N个记录就可以了。当然也可以按照上面的思路来做实现,下面的代码就是按照我们前面的思路来做的实现:

package com.zhyea.dev

import java.util

import org.apache.hadoop.io.{LongWritable, Text}
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.spark.{SparkConf, SparkContext} import collection.JavaConversions.asScalaIterator object TopTen { def main(args: Array[String]): Unit = {
val inputPath = args(0)
val outputPath = args(1)
val conf = new SparkConf().setAppName("Top Ten")
val sc = new SparkContext(conf)
val data = sc.hadoopFile[LongWritable, Text, TextInputFormat](inputPath)
data.mapPartitions[Long](findTopTen)
.repartition(1)
.distinct()
.sortBy(_.toLong, false)
.mapPartitions(itr => itr.slice(0, 10))
.saveAsTextFile(outputPath) def findTopTen(itr: Iterator[(LongWritable, Text)]) = {
val set = new util.TreeSet[Long]()
itr.foreach(p => {
val v = p._2.toString.toLong
if (set.size <= 10) {
set.add(v)
} else if (v > set.first) {
set.pollFirst()
set.add(v)
}
})
set.iterator
} } }

############################

十二道MR习题 - 4 - TopN问题的更多相关文章

  1. 十二道MR习题 - 2 - 多文件保存

    题目: 需要将MR的执行结果保存到3个文件中,该怎么做. 又是一个送分题. 对于Hadoop的MapReduce来说只需要设置一下reduce任务的数量即可.MR的Job默认reduce数量是1,需要 ...

  2. 十二道MR习题 - 3 - 交集并集差集

    题目 有两个文件A和B,两个文件中都有几百万行数字,现在需要找出A文件和B文件中数字集合的交集.并集.以及A对B的差集. 简单说一下思路: 这个问题关键在于key和value的设计.这里我将文件中的数 ...

  3. 十二道MR习题 – 1 – 排序

    题目: 一个文件,大小约为100G.文件的每一行都是一个数字,要求对文件中的所有数字进行排序. 对于这个题目,了解过Hadoop的同学可以笑而不语了.即使用spark实现也是非常简单的事情. 先说下如 ...

  4. C primer plus 第五版十二章习题

    看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: /* global.c --- 使用外部变量 ...

  5. hadoop生态系统学习之路(十)MR将结果输出到hbase

    之前讲了MR将结果输出到hdfs.hive.db,今天再给大家分享一下,怎样将结果输出到hbase. 首先,提一句,笔者在hadoop集群运行此MR的时候报了一个错误.是一个jar包的缘故,这个错误是 ...

  6. C和指针 第十六章 习题

    16.8 计算平均年龄 #include <stdlib.h> #include <stdio.h> #define MAX_LEN 512 int main() { int ...

  7. C和指针 第十五章 习题

    15.8 十六进制倾印码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include & ...

  8. C和指针 第十四章 习题

    14.1 打印函数 #include <stdio.h> void print_ledger_long(){ printf("function print_ledger_long ...

  9. 视觉slam十四讲习题ch3-6

    题目回顾: 一般解线性方程Ax=b有哪几种做法?你能在Eigen中实现吗? 解: 线性方程组Ax = b的解法 : 1.直接法:(1,2,3,4,5) 2.迭代法:如Jacobi迭代法(6) 其中只有 ...

随机推荐

  1. nodejs 循环中操作需要同步执行解决方案

    最近用nodejs做了个针对某网站的小爬虫.干坏事得低调对吧,不能同时开太多的网络访问,结果各种回调/循环虐的心力交瘁. 经过了n次的百度\哥哥后终于拼出了自己要的功能.不敢独享分享出来以供大家参考. ...

  2. k8s更新Pod镜像

    实际使用k8s中,如果使用RC启动pod可以直接使用滚动更新进行pod版本的升级,但是我们使用的情况是在pod里面启动有状态的mysql服务,没有和RC进行关联,这样更新的时候只能通过 更新pod的配 ...

  3. c# WinForm软件启动拦截(通过更改文件关联实现)

    前几天想做一个软件启动之前拦截的程序,找了下网上的资料没有找到合适的,突然看到电脑软件某看图软件,找到个思路就是跟他一样的,通过修改文件关联进行启动拦截. 原理是这样的,更改.exe默认的启动方式为我 ...

  4. docker在团队中的实践 How To Install Docker In CentOS

    " 预发布机器(centos-6.5),给每个同学都开通了ssh这个机器是大家一起共用的,稍后导些数据下来.后续 项目上线,产品测试,都是在这上面进行.  目前在一个物理机 " 3 ...

  5. IO流入门-第十章-DataInputStream_DataOutputStream

    DataInputStream和DataOutputStream基本用法和方法示例 /* java.io.DataOutputStream 数据字节输出流,带着类型写入 可以将内存中的“int i = ...

  6. <2014 05 14> Android平台下2D/3D开发攻略

    Android通过OpenGL包含了对高性能2D和3D图形的支持,尤其支持OpenGLES API.OpenGL是一个跨平台的图形API,提供了软件操作3D图形硬件的接口.OpenGLES是一个专用于 ...

  7. java 内存空间

    堆:new 出的对象在堆上 java栈:java程序.线程运行数据.内存数据 每个方法都有自己的栈.运行时需要的数据存在自己的栈中 每个线程对立的是图中浅蓝色的部分(java栈.本地方法栈.程序计数器 ...

  8. maven安装,maven命令行使用

    1 下载maven,解压(无需安装),配置环境变量,命令行mvn -v测试. 2 maven常用命令:https://www.cnblogs.com/wkrbky/p/6352188.html 3 注 ...

  9. caffe使用(1)

    caffe使用 caffe是一个卓越的CNN框架 caffe源码是Cpp语言的,基于一些外部的库,包括BLAS(矩阵计算),CUDA(GPU驱动),gflags,glog,boost,protobuf ...

  10. 关于session的常用用法

    (一)django有四中session实现方式 1.数据库(database-backed sessions) 2.缓存(cached sessions) 3.文件系统(file-based sess ...