理解 map 和 reduce 的实现

我们来思考一个题目,有一个 List List list = new ArrayList(1, 2, 3, 4, 5) ,我想实现这样的一个功能,把 list 里面的每个元素乘以 2 ,用 Java 怎么写?

import java.util.ArrayList;

public class ListDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        ArrayList<Integer> result = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            result.add(list.get(i) * 2);
        }
    }
}

如果我现在不想乘以 2 ,我想将逻辑做一下变化,现在不乘以 2 了,要加 3 ,那怎么办?

import java.util.ArrayList;

public class ListDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        ArrayList<Integer> result = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            result.add(list.get(i) * 2);
        }

        ArrayList<Integer> result1 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            result1.add(list.get(i) * 3);
        }
    }
}

如果这么写,很多的代码是一样的,只有一个地方不一样。从 list 中拿出来的一个元素,做映射,转变成另外一个元素,然后设置到新的 list 中去,中间的这个映射逻辑我们没搞定,为了提高这个代码的通用性,我们应该怎么做?我希望以后不管是什么逻辑,不用写重复的这一堆代码。

sc
  .textfile("hdfs://hadoop277ha/spark/wc/input/words.txt")
  .flatMap(_.split(" "))
  .map((_, 1))
  .reduceByKey(_ + _)
  .saveAsTextFile("hdfs://hadoop277ha/spark/wc/output")

在 Scala 的 WordCount 程序里面有这样一段代码,这个 map 的工作就是你给我一个单词,我把单词变成单词对返回。这个里面的逻辑 map 做了什么?就是上一个结果集当中每次拿出来一个元素,然后交给这个逻辑,这个逻辑最终把那个元素变成了一个单词对,这就是所谓的映射。我们能不能也去做这个事儿呢?

public class ListMapReduce {
    // list.map(x => x+1)
    // map 方法有吗? x=>x+1 计算逻辑,方法,Java 当中,方法里面能传方法作为参数吗?
    private List list;

    public List map() {
        ArrayList<Integer> newResult = new ArrayList<>();
        return newResult;
    }
}

list.map Java 中 list 没有这个方法,我想实现这个功能,给我一个 x ,我帮你把 x 加 1 ,这里面有两个难点,List 当中没有 map 方法,x 变成 x+1 是个计算逻辑,我们可以认为在 Java 中定义计算逻辑的东西是一个方法。
假设有 map 方法,但是在 Java 当中,方法里面能传方法作为参数吗?也不能。但是我们有方式可以去实现,既然没有这个 map 方法给 list 直接去调用,但是我可以定义一个 map 方法作用在这个 list 之上,我们有个成员变量 list 存数据,跟 map 方法是平级的,这个 map 方法可以操作这个 list 。
另外,x 变成 x+1 是个计算逻辑,map 方法需要一个参数,这个参数该怎么定义呢,不能往里面传一个方法,但我们可以把这个逻辑放在某个对象里面,传这个对象,我往 map 里面传一个对象,这个对象里面有一个方法,这个方法就是我想要的计算逻辑。这个对象现在是什么我可能不太知道,但是我希望它里面有一个方法能够帮我去执行计算逻辑,这肯定是一个映射。

import java.util.ArrayList;
import java.util.List;

public class ListMapReduce {
    // list.map(x => x+1)
    // map 方法有吗? x=>x+1 计算逻辑,方法,Java 当中,方法里面能传方法作为参数吗?
    private List<Integer> list;

    public List map(Operator o) {
        ArrayList<Integer> newResult = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            int result = o.f(list.get(i));
            newResult.add(result);
        }
        return newResult;
    }
}

interface Operator {
    int f(int a);
}

这个方法具不具备通用性呢?我们做一个测试,我们看 result1 这个结果,我针对 listMapReduce 做了一个 map 操作,里面 map 的具体实现是在遍历成员变量 list ,然后把每个元素拿出来,用 o 对象当中的 f 方法,做了映射,你把每个元素给我,我给你反馈一个结果,map 是做了一个映射,具体的逻辑怎么做映射,传给 f 的每个元素到底变成什么结果呢?我们在 Operator 接口的方法中实现,这变得非常灵活,我可以加 1 ,可以乘 2 ,无论如何 map 的逻辑不用动,调用逻辑不用动,只需要在 f 里面定义方法,定义具体的实现逻辑,这个方法里面已经写好了一部分,如参数是什么,返回值是什么,需要自己定一个逻辑,然后得到一个返回值。

import java.util.ArrayList;
import java.util.List;

public class ListMapReduce {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        ListMapReduce listMapReduce = new ListMapReduce();
        listMapReduce.setList(list);
        List result1 = listMapReduce.map(new Operator() {
            @Override
            public int f(int a) {
                return a + 1;
            }
        });
    }

    public List<Integer> getList() {
        return list;
    }

    public void setList(List<Integer> list) {
        this.list = list;
    }

    // list.map(x => x+1)
    // map 方法有吗? x=>x+1 计算逻辑,方法,Java 当中,方法里面能传方法作为参数吗?
    private List<Integer> list;

    public List map(Operator o) {
        ArrayList<Integer> newResult = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            int result = o.f(list.get(i));
            newResult.add(result);
        }
        return newResult;
    }
}

interface Operator {
    int f(int a);
}

map 做了什么呢?你给我一个值,这个值从成员变量 list 当中遍历得来,我通过 f 的逻辑,帮你把这个值变成另外一个值,这个值放到最终要返回的 newResult 里面去,这里 new 了一个 newResult ,去装每一次映射得到的结果,把这个 newResult 返回。map 方法在 ListMapReduce 里面,把 ListMapReduce 中的成员变量 list 转成新的List newResult/result1 ,具体怎么转的,映射的逻辑在我们 o 对象的方法 f 里面定义,我们在用的时候,要用什么逻辑,就临时性的创建一个临时接口,创建一个匿名对象。

这个计算逻辑,在 Java 当中,只能够定义方法,那怎么能够让方法传到一个方法里面去呢?在 Java 当中不支持这个语法,我们可以把一个方法定义在一个接口当中,未来我们在用的时候,想要实现什么样的功能,随时随地传一个包含该方法实现的一个对象就可以了。

按照这个思路,我们还可以实现 reduce 。

import java.util.ArrayList;
import java.util.List;

public class ListMapReduce {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        ListMapReduce listMapReduce = new ListMapReduce();
        listMapReduce.setList(list);

        // 调用
        List result1 = listMapReduce.map(new Operator() {
            @Override
            public int f(int a) {
                return a + 1;
            }
        });
        int result2 = listMapReduce.reduce(new ReduceOperator() {
            @Override
            public int reduce(int a, int b) {
                return a + b;
            }
        });
        System.out.println(result2);
    }

    public List<Integer> getList() {
        return list;
    }

    public void setList(List<Integer> list) {
        this.list = list;
    }

    // list.map(x => x+1)
    // map 方法有吗? x=>x+1 计算逻辑,方法,Java 当中,方法里面能传方法作为参数吗?
    private List<Integer> list;

    public List map(Operator o) {
        ArrayList<Integer> newResult = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            int result = o.f(list.get(i));
            newResult.add(result);
        }
        return newResult;
    }

    public int reduce(ReduceOperator o) {
        // 这个地方需要加判断(空、越界)
        int result = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            result = o.reduce(result, list.get(i));
        }
        return result;
    }
}

interface Operator {
    int f(int a);
}

interface ReduceOperator {
    int reduce(int a, int b);
}

以上 map 和 reduce 方法的实现,其实是一个设计模式,叫策略模式,就是不同的业务场景用不同的需求实现,就是不同的情况我有不同的策略去针对你。


微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。关注【数据分析】公众号,后台回复【文章】,获得整理好的【数据分析】文章全集。
数据分析公众号二维码


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值