java 8 stream reduce详解和误区

简介: java 8 stream reduce详解和误区

目录



java 8 stream reduce详解和误区


简介


Stream API提供了一些预定义的reduce操作,比如count(), max(), min(), sum()等。如果我们需要自己写reduce的逻辑,则可以使用reduce方法。


本文将会详细分析一下reduce方法的使用,并给出具体的例子。


reduce详解


Stream类中有三种reduce,分别接受1个参数,2个参数,和3个参数,首先来看一个参数的情况:


Optional<T> reduce(BinaryOperator<T> accumulator);


该方法接受一个BinaryOperator参数,BinaryOperator是一个@FunctionalInterface,需要实现方法:


R apply(T t, U u);


accumulator告诉reduce方法怎么去累计stream中的数据。


举个例子:


List<Integer> intList = Arrays.asList(1,2,3);
        Optional<Integer> result1=intList.stream().reduce(Integer::sum);
        log.info("{}",result1);


上面的例子输出结果:


com.flydean.ReduceUsage - Optional[6]


一个参数的例子很简单。这里不再多说。


接下来我们再看一下两个参数的例子:


T reduce(T identity, BinaryOperator<T> accumulator);


这个方法接收两个参数:identity和accumulator。多出了一个参数identity。


也许在有些文章里面有人告诉你identity是reduce的初始化值,可以随便指定,如下所示:


Integer result2=intList.stream().reduce(100, Integer::sum);
        log.info("{}",result2);


上面的例子,我们计算的值是106。


如果我们将stream改成parallelStream:


Integer result3=intList.parallelStream().reduce(100, Integer::sum);
        log.info("{}",result3);


得出的结果就是306。


为什么是306呢?因为在并行计算的时候,每个线程的初始累加值都是100,最后3个线程加出来的结果就是306。


并行计算和非并行计算的结果居然不一样,这肯定不是JDK的问题,我们再看一下JDK中对identity的说明:


identity必须是accumulator函数的一个identity,也就是说必须满足:对于所有的t,都必须满足 accumulator.apply(identity, t) == t


所以这里我们传入100是不对的,因为sum(100+1)!= 1。


这里sum方法的identity只能是0。


如果我们用0作为identity,则stream和parallelStream计算出的结果是一样的。这就是identity的真正意图。


下面再看一下三个参数的方法:


<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);


和前面的方法不同的是,多了一个combiner,这个combiner用来合并多线程计算的结果。


同样的,identity需要满足combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)


大家可能注意到了为什么accumulator的类型是BiFunction而combiner的类型是

BinaryOperator?


public interface BinaryOperator<T> extends BiFunction<T,T,T>


BinaryOperator是BiFunction的子接口。BiFunction中定义了要实现的apply方法。


其实reduce底层方法的实现只用到了apply方法,并没有用到接口中其他的方法,所以我猜测这里的不同只是为了简单的区分。


总结


虽然reduce是一个很常用的方法,但是大家一定要遵循identity的规范,并不是所有的identity都是合适的。


本文的例子https://github.com/ddean2009/learn-java-streams/tree/master/stream-reduce

相关文章
|
4天前
|
安全 Java API
Java Stream API详解与使用
Java Stream API是Java 8引入的特性,提供函数式操作处理集合,支持链式操作和并行处理,提升代码可读性和性能。关键点包括:延迟执行的中间操作(如filter, map)和触发计算的终端操作(如collect, forEach)。示例展示了如何从Person列表过滤出年龄大于20的姓名并排序。使用Stream时注意避免中间操作的副作用,终端操作后Stream不能复用,以及并行操作的线程安全性。
12 1
|
1天前
|
安全 Java API
Java进阶-Java Stream API详解与使用
效、更易于维护的代码,同时享受到函数式编程带来的好处。
9 2
|
2天前
|
Java 大数据 API
利用Java Stream API实现高效数据处理
在大数据和云计算时代,数据处理效率成为了软件开发者必须面对的重要挑战。Java 8及以后版本引入的Stream API为开发者提供了一种声明式、函数式的数据处理方式,极大提升了数据处理的效率和可读性。本文将详细介绍Java Stream API的基本概念、使用方法和性能优势,并通过实际案例展示如何在实际项目中应用Stream API实现高效数据处理。
|
2天前
|
自然语言处理 Java API
Java 8的Stream API和Optional类:概念与实战应用
【5月更文挑战第17天】Java 8引入了许多重要的新特性,其中Stream API和Optional类是最引人注目的两个。这些特性不仅简化了集合操作,还提供了更好的方式来处理可能为空的情况,从而提高了代码的健壮性和可读性。
24 7
|
2天前
|
Java API
Java 8新特性之Lambda表达式与Stream API
【5月更文挑战第17天】本文将介绍Java 8中的两个重要特性:Lambda表达式和Stream API。Lambda表达式是一种新的编程语法,它允许我们将函数作为参数传递给其他方法,从而使代码更加简洁。Stream API是一种用于处理集合的新工具,它提供了一种高效且易于使用的方式来处理数据。通过结合使用这两个特性,我们可以编写出更加简洁、高效的Java代码。
7 0
|
2天前
|
Java
Java8 Stream 用法合集
Java8 Stream 用法合集
|
4天前
|
Java API
Java 8新特性之Lambda表达式与Stream API实践指南
【5月更文挑战第15天】 随着Java语言的不断发展,Java 8作为一个重要的版本,引入了许多令人兴奋的新特性。其中,Lambda表达式和Stream API是Java 8最受关注的两个特性。本文将深入探讨Lambda表达式的基本概念、语法和使用场景,以及如何结合Stream API实现更加简洁、高效的代码编写。通过实例演示,帮助读者快速掌握这两个新特性,提高Java编程能力。
|
4天前
|
Java API 数据处理
Java一分钟之-Stream API:数据处理新方式
【5月更文挑战第13天】Java 8的Stream API为集合操作提供了声明式编程,简化数据处理。本文介绍了Stream的基本概念、常见问题和易错点。问题包括并行流与顺序流的区别,状态改变操作的影响,以及忘记调用终止操作和误用`peek()`。理解并合理使用Stream API能提升代码效率和可维护性。实践中不断探索,将发掘更多Stream API的潜力。
12 3
|
4天前
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的深度解析
【5月更文挑战第12天】本文将深入探讨Java 8中的两个重要新特性:Lambda表达式和Stream API。我们将从基本概念入手,逐步深入到实际应用场景,帮助读者更好地理解和掌握这两个新特性,提高Java编程效率。
43 2
|
4天前
|
Java API
【JAVA进阶篇教学】第三篇:JDK8中Stream API使用
【JAVA进阶篇教学】第三篇:JDK8中Stream API使用