java和map、reduce

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就分享到这里,感谢阅读!

欢迎关注公众号

<think>好的,用户现在想了解如何在Java中使用Stream API结合mapreduce操作进行数据处理,并请求示例。我需要根据之前的对话内容提供的引用资料来构建回答。首先,回顾用户之前的提问,他们之前询问了关于Java Stream的递增操作,现在转向mapreduce的组合使用,这可能意味着他们正在学习Stream API的不同阶段,需要更深入的应用案例。 首先,我需要确保回答结构清晰,符合用户要求的帮助逐步解决问题。根据用户提供的引用资料,特别是引用[2]中的终端操作示例,包括mapreduce的使用,这可以作为基础。同时,用户之前的回答中提到了map()IntStream的使用,需要将这些知识点连贯起来。 接下来,用户可能的需求是理解如何将mapreduce结合使用,比如数据转换后聚合。需要提供具体的示例,如计算平方、字符串处理、对象属性求等。同时,要注意可能的异常情况,比如空流处理,这在引用[3]中提到了空流的创建,可能需要涉及。 另外,用户之前的回答中提到了性能优化,如使用基本类型流(IntStream)避免装箱,所以在示例中可以对比展示使用IntStream普通Stream的区别,引用[3]中的例子可以作为参考。同时,引用[1]中的异常提示需要注意流不能被重复使用,因此在示例中要强调流的单次使用特性,避免用户操作已关闭的流。 还要注意用户可能遇到的常见错误,比如在reduce中的初始值选择并行流中的问题,引用[5]提到了并行流的使用,可能需要提及并行处理时的注意事项。例如,在reduce操作中,累加器需要满足结合律,否则并行计算会出错。 此外,用户可能需要知道mapreduce的具体作用:map用于转换数据,reduce用于聚合结果。结合引用[2]中的reduce示例,可以扩展更复杂的案例,如计算平均值或者最大值,但需要结合map先进行数据提取或转换。 最后,生成相关问题,帮助用户进一步学习,比如比较mapflatMap,或者讨论并行流的使用,这可以引用之前的回答引用中的内容,确保问题相关且有参考价值。 总结回答结构: 1. 简介mapreduce的作用 2. 基础示例(数值计算、字符串处理、对象处理) 3. 进阶用法(空值处理、并行流) 4. 原理说明(引用之前的回答引用资料) 5. 相关问题 需要确保代码示例正确,语法无误,并且注释清晰。同时,引用资料中的例子可以作为基础,但需要根据用户的问题进行调整扩展。例如,引用[2]中的reduce示例是求整数列表的,可以扩展为结合map先进行平方再求,以展示两者的结合使用。 还要注意用户提到的异常情况,比如流不能重复使用,这可能在示例中通过正确的流操作来避免。例如,在示例中每次操作都新建流,而不是重复使用已关闭的流。 最后,确保使用中文回答,格式符合要求,行内数学公式用$...$,独立公式用$$...$$,虽然问题中可能不需要数学公式,但如果有涉及计算的地方可能需要使用。例如,在计算平均值时,可以用公式表示。不过当前示例可能不需要,所以可能可以忽略。 现在,综合所有信息,组织回答内容,确保覆盖用户的问题,并参考之前的引用回答,提供详细且易懂的示例解释。</think>以下是通过Java Stream API结合`map``reduce`实现数据处理的详细指南,包含原理说明代码示例: --- ### 一、核心概念与基础示例 #### 1. **`map()`的作用** - **数据转换**:将流中元素转换为新值或新类型 - **示例**:将字符串转换为大写 ```java List<String> words = Arrays.asList("apple", "banana"); List<String> upperWords = words.stream() .map(String::toUpperCase) // 转换为全大写 .collect(Collectors.toList()); // [APPLE, BANANA] ``` #### 2. **`reduce()`的作用** - **聚合操作**:将流元素合并为单个结果 - **示例**:计算整数列表的 ```java List<Integer> numbers = Arrays.asList(3, 2, 5); int sum = numbers.stream() .reduce(0, Integer::sum); // 初始值0,累加方式为求 System.out.println(sum); // 10 ``` --- ### 二、`map`与`reduce`联合使用场景 #### 1. **数值计算:平方** ```java List<Integer> nums = Arrays.asList(2, 3, 4); int squareSum = nums.stream() .map(n -> n * n) // 转换为平方值 [4, 9, 16] .reduce(0, Integer::sum); // 累加结果:29 ``` #### 2. **字符串处理:连接字符串** ```java List<String> texts = Arrays.asList("Java", "Stream", "API"); String combined = texts.stream() .map(s -> s.toLowerCase()) // 转为小写 ["java", "stream", "api"] .reduce("", (a, b) -> a + "|" + b); // 拼接为 "|java|stream|api" ``` #### 3. **对象处理:计算订单总价** ```java class Order { String product; double price; // 构造方法/getter省略 } List<Order> orders = Arrays.asList( new Order("Laptop", 1200), new Order("Phone", 800) ); double totalPrice = orders.stream() .map(Order::getPrice) // 提取价格 [1200.0, 800.0] .reduce(0.0, Double::sum); // 合计2000.0 ``` --- ### 三、进阶用法 #### 1. **空值处理** ```java List<Integer> maybeNull = Arrays.asList(5, null, 7); int safeSum = maybeNull.stream() .filter(Objects::nonNull) // 过滤null值 .map(n -> n * 2) // 非空元素翻倍 [10, 14] .reduce(0, Integer::sum); // 结果24 ``` #### 2. **并行流优化** ```java List<Integer> bigData = /* 大数据集 */; int parallelSum = bigData.parallelStream() // 并行处理 .map(n -> n * n) .reduce(0, Integer::sum); // 确保累加器满足结合律[^5] ``` --- ### 四、原理说明 1. **`map`的惰性求值**:`map`是中间操作,仅在终端操作(如`reduce`)触发时执行[^2] 2. **`reduce`的初始值选择**:若流可能为空,必须提供初始值以避免`Optional`返回[^3] 3. **并行流限制**:`reduce`操作的累加函数需满足**结合律**(如加法、乘法),否则并行计算会出错[^5] --- ### 相关问题 1. 如何用`map``reduce`实现数据分组统计? 2. `reduce`方法的`BinaryOperator`参数如何自定义? 3. 并行流中`map`操作是否线程安全?需要注意什么? --- 通过上述示例原理分析,可以更清晰地掌握`map`与`reduce`的组合应用。若遇到流重复使用导致的`IllegalStateException`,需注意**流只能被消费一次**的特性[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值