MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)“和"Reduce(归约)”,是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。
——摘自《百度百科》
概念什么的最无聊了,就说说map reduce能干啥事:
简单来说map操作是对一个集合中所有元素执行指定的函数操作完事返回一个新的集合。
reduce操作接收两个参数,第一个参数是上次计算的结果,第二个参数是集合中当前迭代的元素,当然第一次迭代时两个参数就是第一个和第二个元素。
在java中jdk1.8以后集合可以直接获取stream对象,stream对象支持map和reduce等一系列方法。
来看看这些栗子:
现在有一个集合需要对集合中每个元素求平方返回一个新的集
List<Integer> integers = Arrays.asList(1, 3, 5, 7, 9);
List<Integer> mapIntegers =
integers.stream().map(x -> x * x).collect(Collectors.toList());
System.out.println(mapIntegers);
输出:
// [1, 9, 25, 49, 81]
解释一下代码:
integers.stream() 获取集合的stream对象
.map(x -> x * x).collect(Collectors.toList()); 对stream对象进行map操作,map接收一个Function<T,R>函数式接口,如果不了解函数式接口请移步java——lambda表达式,函数式编程,传入计算的lambda表达式,map便会对每一个元素执行lambda中的操作并将lambda中的返回值收集到一个新的stream对象中作为map的返回值返回;而collect操作将stream对象中的每一个对象重新收集起来并返回参数指定的类型,栗子中是 Collectors.toList() 也就是List集合类型。
2. 现在有一个集合,需要对集合中所有的元素进行求和
List<Integer> integers = Arrays.asList(1, 3, 5, 7, 9);
Optional<Integer> sum =
integers.stream().reduce((x, y) -> x + y);
System.out.println(sum.get()); // 25
可以看到reduce返回的是一个Optional类型,也就是说可能返回的是空。reduce 中首先第一次循环取第一个和第二个元素调用lambda表达式,如果集合后面还有数据,reduce 会将上次计算结果作为第一个参数传入lambda表达式,将第三个元素传入第二个参数,以此类推最后返回一个Optional对象。
也许你会问如果集合只有一个元素呢?实验结果表明如果只有一个元素reduce会直接返回这个数字而不会执行lambda操作
List<Integer> integers = Arrays.asList(1);
Optional<Integer> sum =
integers.stream().reduce((x, y) -> ++x + y);
System.out.println(sum.get()); // 输出 1
结果并没有自增,然而:
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
Optional<Integer> sum =
integers.stream().reduce((x, y) -> ++x + y);
System.out.println(sum.get()); // 输出 13
显然4个元素调用了三次lambda表达式,也证明了之前的推断。
3. 有一个订单信息,该订单包含了若干商品每个商品有单价(price)和数量(total),求订单总金额。
商品类:
public class Good {
private float price;
private int total;
public Good(float price, int total) {
this.price = price;
this.total = total;
}
// Getter Setter ...
}
main:
public static void main(String[] args){
List<Good> order = new ArrayList<>();
order.add(new Good(10.5f, 2));
order.add(new Good(20.5f, 1));
order.add(new Good(30.5f, 3));
order.add(new Good(40.5f, 4));
Float totalPrice = order.stream()
.map(g -> g.getPrice() * (float) g.getTotal())
.reduce((x, y) -> x + y).get();
System.out.println(totalPrice); // 295.0
}
先使用map让每一个商品的单价和数量相乘,再使用reduce对map结果进行求和操作,是不是比for循环简单多了!
其实java提供了更简单的方法:
double sum = order.stream()
.mapToDouble(x -> x.getPrice() * x.getTotal())
.sum();
是不是比上面写法更简单,java默认为map提供了三个方法:
mapToInt、mapToLong、mapToDouble供能类似
map还支持flatMap、flatMapToDouble、flatMapToInteger、faltMapToLong操作,来对stream对象进行扁平化管理。
List<String> s = Arrays.asList("how ", "are ", "you");
List<String> collect = s.stream()
.flatMap(x -> Arrays.stream(x.split("")))
.collect(Collectors.toList());
System.out.println(collect);
// [h, o, w, , a, r, e, , y, o, u]
可以看到将每个字符串切割成字符之后又组合成了一个新的集合,
其实lambda部分可以拆分成这样:
List<String> collect = s.stream().flatMap(x -> {
String[] ss = x.split("");
Stream<String> stringStream = Arrays.stream(ss);
return stringStream;
}).collect(Collectors.toList());
可以看到lambda返回值和map是不一样的,返回的是一个stream对象,flatMap将这些对象重新整合成一个流。
好了今天的map reduce就分享到这里,感谢阅读!