小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。 据说MapReduce框架借鉴了函数式编程的思想,利用Map和Reduce两个操作来实现对数据的各种操作。
而当今大部分的大部分的语言都支持了函数式的流操作,比如Java 8 的 Stream 流。再看其他的语言,诸如Python,Koltin,JavaScript之类的语言这些函数式操作(map, reduce类)更是像喝水一样自然,没有用过都不好意思说自己接触过这些语言。除此之外,甚至连C++都开始支持这类操作了(C++ 20的 ranges 范围库)。
在上述这些语言中,map是一个比较简单的操作,通过对每一个元素执行一个同样的操作(transform),变为另一个元素。然后整个元素集合相当于转变成由结果组成的元素集合(毕竟实际上有的及早求值,而有的方法为了降低开销是惰性求值)。对于我们用户来讲,只用关心对每个元素的操作即可。而不必要过多关注整个元素集合。
从效果来看,map相当于是下面这样子的流程(我们这里就不关心惰性求值了),其中tranform是我们传入的转换用的函数。
val destination = SomeDestination()
for (item in orginCollection)
destination.add(transform(item))
return destination
现在,我们把它和 mapper 中的 map 操作做对比。
Map Reduce 框架中的所谓 Mapper 类的 map 方法如下。
protected void map(KEYIN key, VALUEIN value, Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
// context.write(keyout, valueout);
}
上面是使用最频繁的方法,我们在用map reduce框架时对数据进行转换就是靠这个操作。它给我们暴露了一个操作 context.write(key, value)用来输出结果,这与我们平常使用的map 差别还是有点大。我想首先把他变成类似我们先前map类似的操作来对比(与我们熟悉的内容做对比)。假设我们把这个所谓的Context当作ArrayList来看待。他应该像这样:
fun map(key: K, value: V, context: ArrayList<Pair<KEYOUT, VALUEOUT>>): Unit {
// context.write(keyout to valueout)
}
每当我们执行一个转换操作(在这里就是map),我们在map的末尾添加一个键值对(keyout ,valueout) 。
再我们往回看mapper要求我们实现的类,可以发现他其实还是不一样的。context的write更接近于Java中的双列集合的put操作。把键和值分为物理意义上的两部分。从我个人的角度来说,我更倾向于逻辑意义上的key,value。在需要key的时候,通过某种函数之类的操作,指定相应的key。
而Map Reduce的考虑则是,进行物理上的切分,要求key必须可以比较(也就是实现比较操作),而value则随意。
再看函数入口,会发现它有个奇奇怪怪的key参数。它实际上是一个偏移量。暂时可以当作不存在。value才是我们需要处理的数据。我们实际上实际对value进行转换操作。
比如经典的map reduce的 hello world 程序单词计数,其中的mapp操作需要我们为每个单词计数为1,后续交给reduce函数做统计。
private final static IntWritable one = new IntWritable(1);
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
context.write(word, one);
}
但其实,这个map并非仅仅只能做一对一的映射。程序把结果的写入方法交给了我们,这也就意味着,我对它由控制的能力,我不但可以干转换这一件事,还可以干其他的活。
- 比如我们可以选择,不写入,这将会相当于一个filter操作,通过选择性的写入,无用的数据可以在这一步被清理。
- 或者我们可以写多个数据,例如,默认情况下,mapreduce以数据的每一行作为传入值交给我们操作。而如果行内的值也可以解析成多个(比如单词统计中一行有多个单词),我们可以把解析出来的每一个单词写入结果。那么实际上,这相当于我们平常用的flatMap操作。
小结
Map Reduce 中的Mapper尽管借鉴了函数式的思维,但是其中的Mapper功能有点多,与我们平常使用的map有相似之处,与flatMap更类似一些。而且它经常可能包揽map和filter的工作,我个人认为它其实了承担过多的职责。